2024-02-26
24T1
00

目录

GPT 链接
所有权
变量被丢弃
owned与copy
什么是Copy类型
我能否手动添加copy
借用/引用 Borrowing
不可变借用(独占借用)
可变借用(共享借用)
借用规则
共享异或可变 Shared 'XOR' Mutable
共享访问 Shared
可变访问 Mutable
独占借用的注意事项
在独占借用的生命周期内,不能再声明任何其他的独占借用。
在独占借用的生命周期内,不能再有任何共享借用。
运用借用Borrow的原则

Ownership & Borrowing

GPT 链接

Shared Link

所有权

所有权是Rust最独特的特性之一,旨在提供内存安全而不需要垃圾收集。在Rust中,每个值都有一个被称为其"所有者"的变量。值有以下规则:

  • 值在任何时候只能有一个所有者。
  • 当所有者(变量)离开作用域,其值将被丢弃。

变量被丢弃

一个方法使用了某一个变量,然而没有进行值的返回。在这个过程中,该变量的所有权被该方法所独享,其他函数无法再使用该变量。

解决方法为,尝试让该函数返回使用后的变量,以此达到所有权的回归。

RUST
fn main() { let img = Image::new(256,256); //变量被使用 let sist = use_img(img); } fn use_img(mut img::Image) { ..... ..... //返回该变量以回归所有权 img }

owned与copy

什么是Copy类型

Copy trait是一个标记trait,用于指示类型的值可以通过简单的位复制来"复制",而不是被移动。当一个类型实现了Copy trait时,它的值在被赋值给另一个变量时不会转移所有权。这意味着原始变量在赋值后仍然可以使用,因为内存中的数据被复制到了新的位置。

不是所有类型都可以实现Copy trait。只有那些不涉及堆分配或资源管理(如文件句柄)的简单类型(如整数、浮点数、布尔值、字符以及这些类型的固定大小数组和元组)默认实现了Copy trait。复杂类型,如StringVec<T>,不实现Copy,因为它们在堆上管理内存,其所有权在赋值时需要被转移,以避免双重释放。

RUST
fn main() { let x = 5; // x 是一个整数类型,实现了 Copy trait let y = x; // y 是 x 的一个副本,x 不会失去所有权,因为 i32 实现了 Copy trait println!("x: {}, y: {}", x, y); // x 和 y 都可以使用,因为 i32 类型的值被复制了 }
RUST
fn main() { let s1 = String::from("hello"); // s1 是一个 String 类型,不实现 Copy trait let s2 = s1; // s1 的所有权移动到 s2,s1 不再有效 // println!("s1: {}, s2: {}", s1, s2); // 这行会编译错误,因为 s1 的值已经移动了 println!("s2: {}", s2); // 只能使用 s2 }

我能否手动添加copy

当你的复杂字段内部是简单类型时,你可以在声明的时候进行自动派生COPY选项。

RUST
#[derive(Debug, Clone, Copy)] struct Point { x: i32, y: i32, } fn main() { let point1 = Point { x: 5, y: 10 }; let point2 = point1; // 因为 Point 实现了 Copy trait,所以这里是复制而不是移动 println!("point1: {:?}", point1); // 有效,因为 point1 的值被复制到 point2 println!("point2: {:?}", point2); }

然而,copy仅适合小型而简单的类型。如果类型涉及到内存管理/堆内存分配(例如String和Vec),或者需要特别管理的资源(如文件句柄、网络套接字),就需要使用Clone来提供更复杂的逻辑。

借用/引用 Borrowing

在Rust中,借用(borrowing)是一种允许你访问数据而不取得其所有权的机制。这是Rust内存安全保证的核心部分之一,旨在无需垃圾收集即可防止数据竞争等问题。借用在Rust中主要通过引用来实现,分为两种类型:不可变引用(&T)和可变引用(&mut T)。

不可变借用(独占借用)

不可变借用允许你读取数据,但不允许修改它。当数据被不可变借用时,原始数据仍然可以被多次借用为不可变引用,但不能被借用为可变引用。这保证了在不可变借用的有效期内,数据不会被意外改变。

RUST
fn main() { let data = vec![1, 2, 3]; let ref1 = &data; // 不可变借用 let ref2 = &data; // 另一个不可变借用,与 ref1 同时存在是允许的 println!("{:?} {:?}", ref1, ref2); // 可以同时访问 ref1 和 ref2 }

可变借用(共享借用)

可变借用允许你修改数据。当数据被可变借用时,在该借用的有效期内,原始数据不能被其他任何借用(不可变或可变)。这确保了在修改数据时,没有其他引用指向该数据,防止了数据竞争。

RUST
fn main() { let mut data = vec![1, 2, 3]; let ref1 = &mut data; // 可变借用 ref1.push(4); println!("{:?}", ref1); // 此时只能访问 ref1,不能创建其他任何借用 }

借用规则

  • 同时存在的不可变引用数量不限:你可以有任意数量的不可变引用,因为它们不会改变数据。
  • 只能有一个可变引用:在特定作用域中,只能有一个可变引用,防止数据竞争。
  • 不可变和可变引用不能同时存在:如果你有一个可变引用,那么就不能再有不可变引用,反之亦然。这规则确保了数据安全和一致性。【参考后文 共享异或可变】

共享异或可变 Shared 'XOR' Mutable

在Rust中,"Shared XOR Mutable"(共享异或可变)原则是指对于给定的资源,你可以拥有多个不可变引用(共享访问),或者一个可变引用(独占访问),但不能同时拥有。这是Rust内存安全原则的核心之一,旨在在编译时防止数据竞争和其他并发错误。

共享访问 Shared

当你通过不可变引用访问数据时,你正在进行共享访问。Rust允许你同时拥有多个这样的引用,因为它们都保证不会修改数据。这意味着,只要数据不被修改,就可以安全地从多个地方读取它,不会引起数据竞争或其他并发问题。

RUST
let value = 42; let ref1 = &value; // 第一个不可变引用 let ref2 = &value; // 第二个不可变引用 println!("{} {}", ref1, ref2); // 可以安全地同时访问

可变访问 Mutable

当你通过一个可变引用修改数据时,你正在进行独占访问。在这种情况下,Rust确保在该可变引用的生命周期内,没有其他的引用(无论是可变的还是不可变的)指向同一数据。这一约束确保了在修改数据时,没有其他代码可以读取或修改这些数据,从而防止了数据竞争。

RUST
let mut value = 42; let ref1 = &mut value; // 可变引用 *ref1 += 1; // 可以安全地修改数据

独占借用的注意事项

在独占借用的生命周期内,不能再声明任何其他的独占借用。

当你对某个值进行独占借用时,这意味着你获取了对该值的可变引用。在这个可变引用存在的整个生命周期内,你不能再创建任何其他的对该值的独占借用。这是因为Rust的安全规则确保当你有一个可变引用时,该引用是唯一访问和修改值的方式,从而避免了潜在的数据竞争。

RUST
let mut value = 10; let mut_borrow1 = &mut value; // 第一个独占借用 let mut_borrow2 = &mut value; // 错误:不能在 mut_borrow1 存在时创建另一个独占借用

在独占借用的生命周期内,不能再有任何共享借用。

因为共享亦或可变原则。

当你有一个值的独占借用时,你也不能创建该值的任何共享借用。这保证了当数据可能被修改时,没有其他代码可以读取这些数据,因为即使只是读取,也可能会因为数据的改变而导致不一致性或其他错误。

RUST
let mut value = 10; let mut_borrow = &mut value; // 独占借用 let borrow = &value; // 错误:不能在 mut_borrow 存在时创建共享借用

运用借用Borrow的原则

  • 优先使用共享借用。
  • 当你需要修改值的时候,再去用可变借用。

本文作者:Jeff Wu

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!