根据 rust book,ownership 是 rust 用来管理内存空间的生命周期的机制。
类似 C++的 RAII,rust 会在一个结构体的生命周期结束时释放内存,并调用 drop 函数 。
同时,String 类型的赋值会导致 ownership 转移,类似于 C++的 move, 在 rust 中这是默认的。
例如下面这段会抛异常:
1 2 3 4 let s1 = String ::from ("hello" );let s2 = s1;println! ("{}, world!" , s1);
但是下面这段不会:
1 2 3 4 let x = 5 ;let y = x;println! ("x = {}, y = {}" , x, y);
这是由于部分类型实现了 Copy trait,而 String 实现了 Drop trait, 这俩是互斥的。
正因为这样的特性,当传参时,如果 String 被直接传入,那这个 String 相当于已经 move 了,不能再被试用.
因此如果只使用默认的传参方式,calculate_length
函数会被写成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 fn main () { let s1 = String ::from ("hello" ); let (s2, len) = calculate_length (s1); println! ("The length of '{}' is {}." , s2, len); } fn calculate_length (s: String ) -> (String , usize ) { let length = s.len (); (s, length) }
这显然不太方便,因此后面介绍了 reference,也就是写成下面这样,这里标注引用的语法和 C++是反的,同时这里看起来是类似 C++的引用,但是因为没有更复杂的语法,所以实际上可以认为是传指针了。
1 2 3 4 5 6 7 8 9 10 11 fn main () { let s1 = String ::from ("hello" ); let len = calculate_length (&s1); println! ("The length of '{}' is {}." , s1, len); } fn calculate_length (s: &String ) -> usize { s.len () }
上面这个行为在 rust 中被称作 borrowing。 为了安全,reference 默认也是不可变的,调用 String 的方法例如下面这个是会报错的:
1 2 3 4 5 6 7 8 9 fn main () { let s = String ::from ("hello" ); change (&s); } fn change (some_string: &String ) { some_string.push_str (", world" ); }
1 2 3 4 5 6 7 8 9 10 11 12 $ cargo run Compiling ownership v0.1.0 (file:///projects/ownership) error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference --> src/main.rs:8:5 | 7 | fn change(some_string: &String) { | ------- help : consider changing this to be a mutable reference: `&mut String` 8 | some_string.push_str(", world" ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable For more information about this error, try `rustc --explain E0596`. error: could not compile `ownership` due to previous error
所以又要传引用又要改的话,这里会有更复杂的语法
1 2 3 4 5 6 7 8 9 fn main () { let mut s = String ::from ("hello" ); change (&mut s); } fn change (some_string: &mut String ) { some_string.push_str (", world" ); }
这种方式实际上有更复杂的约束条件:同一个变量不能被 mutable borrow 两次, 这样的话 Rust 就从语法层面限制了 data race.
不仅 mutable borrow 不能被重复 borrow, 如果之前被 immutalbe borrow, 那么这个变量也无法被 mutable borrow 了。
错误示范如下:
1 2 3 4 5 6 7 let mut s = String ::from ("hello" );let r1 = &s; let r2 = &s; let r3 = &mut s; println! ("{}, {}, and {}" , r1, r2, r3);
不仅如此,reference 还有个特性,就是不允许跑出变量自身的作用域,例如下面这么用就不行:
1 2 3 4 5 6 7 8 9 fn main () { let reference_to_nothing = dangle (); } fn dangle () -> &String { let s = String ::from ("hello" ); &s }
读后感 这应该就是 Rust 最核心的机制了,不考虑 unsafe 的情况,这种 ownership 的设计保证了内存安全。每块内存只能被一个值拥有,可以通过引用去 borrow,而内存一旦被借用,就不能再去改它,只有借用结束了才能再改它。