2. 变量与数据¶
2.1. 所有权¶
2.1.1. 所有权规则¶
1、rust 中的每个值都有一个被称为所有者(owner)的变量
2、值在任意时刻有且只有一个所有者
3、当所有者(变量)离开作用域,这个值会被丢弃
2.1.2. 内存与分配¶
String
类型,为了支持一个可变,可增长的文本片段,需要在堆上分配一块在编译时未知大小的内存来存放内容。这意味着:必须在运行时向操作系统请求内存。
需要一个当我们处理完
String
时将内存返回给操作系统的方法。
当变量离开作用域,Rust 为我们调用一个特殊的函数。这个函数叫做
drop
,在这里String
的作者可以放置释放内存的代码。Rust 在结尾的}
处自动调用drop
。
2.1.2.1. 变量与数据交互的方式(一):移动¶
Rust 永远也不会自动创建数据的 “深拷贝”。因此,任何 自动 的复制可以被认为对运行时性能影响较小。
String
会申请内存资源,复制时涉及到 内存(值)的所有权转移,参见所有权规则 1fn move_test(){ // 像整型这样的在编译时已知大小的类型被整个存储在栈上 let stack_str = "stack"; let copy_stack = stack_str; println!("{}, hello!, copy {} ", stack_str, copy_stack); // String 类型被分配到堆上,所以能够存储在编译时未知大小的文本。 let heap_str = String::from("heap"); // String实例 heap_str 中指向堆存储的指针作废,move_pointer 复制或者说移动刚刚的指针 // 拷贝指针、长度和容量而不拷贝数据 let move_pointer = heap_str; println!("{0}, hello!; move stack pointer, {0} data no change", move_pointer); }
2.1.2.2. 变量与数据交互的方式(二):克隆¶
会重新生成一个
fn clone_test(){ let heap_str = String::from("hello"); // clone 会深度复制 String 中堆上的数据,而不仅仅是栈上的数据 let heap_copy = heap_str.clone(); println!("heap_str = {}, copy = {}", heap_str, heap_copy); }
2.1.2.3. 只在栈上的数据:拷贝¶
任何简单标量值的组合可以是
Copy
的,不需要分配内存或某种形式资源的类型是Copy
的所有整数类型,比如
u32
。布尔类型,
bool
,它的值是true
和false
。所有浮点数类型,比如
f64
。字符类型,
char
。元组,当且仅当其包含的类型也都是
Copy
的时候。比如,(i32, i32)
是Copy
的,但(i32, String)
就不是。
2.1.2.4. 所有权转移¶
将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时,其值将通过
drop
被清理掉,除非数据被移动为另一个变量所有。函数参数可转移所有权:(copy)/(move)。目前来看取决于传递的类型 是标量值还是占据内存资源的值
返回值可转移所有权
// gives_ownership 将返回值move给调用它的函数 fn gives_ownership() -> String { let some_string = String::from("hello"); // some_string 进入作用域. some_string // 返回 some_string 并移出给调用的函数 } // takes_and_gives_back 将传入字符串并返回该值 fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域 "change".to_owned() // 返回并move给调用的函数 } // a_string 移出作用域并被丢弃。 fn makes_copy(some_integer: i32) { // some_integer 进入作用域 println!("{}", some_integer); } // 这里,some_integer 移出作用域。不会有特殊操作 fn ownership_test() { let s1 = gives_ownership(); // gives_ownership 将返回值move 给s1 let s2 = String::from("hello"); // s2 进入作用域 // s2 被move到函数中,它也将返回值移给 s3。 // 根据规则二: s2已经没有所有权, 不能使用 let s3 = takes_and_gives_back(s2); println!("s1 = {}, s3 = {}",s1, s3); let x = 5; // x 进入作用域 makes_copy(x); // x 应该移动函数里,但 i32 是 Copy 的,所以在后面可继续使用 x } // 根据规则三: 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走, // 所以什么也不会发生。s1 移出作用域并被丢弃
2.2. 引用¶
2.2.1. 规则¶
在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。
引用必须总是有效的。
2.2.2. 实例¶
可变引用/
fn reference_test() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len); // 在特定作用域中的特定数据只能有一个可变引用。可以通过代码块来变通约束条件 { let r1 = &mut s1; } // r1 在这里离开了作用域,所以我们完全可以创建一个新的引用 let r2 = &mut s1; } fn calculate_length(s: &String) -> usize { // s 是对 String 的引用; 获取引用作为函数参数称为 借用(borrowing) s.len() }// 这里,s 离开了作用域。但因为它并不拥有引用值的所有权,所以什么也不会发生