所有权
-
Rust 中的每一个值都有一个被称为其 所有者(owner)的变量。
-
值在任一时刻有且只有一个所有者。
-
当所有者(变量)离开作用域,这个值将被丢弃。
对于规则二:
// 此时"hello"的所有者为 a
let a = String::from("hello");
// move occurs
// because `a` has type `String`, which does not implement the `Copy` trait
// 因为 String 没有实现`Copy` trait,所以"hello"的所有权转移给了 b
let b = a;
// 错误!a已经失去了所有权,不能再访问 "hello"
// println!("{}", a);
// 对于实现了`Copy` trait的类型, 如所有的基础类型(i32, u32, ... 包括 str)
// 此时"hello"的类型为: &str
let a = "hello";
let b = a;
println!("{} {}", a, b);
// 输出:hello hello
对于规则三:
{
let a = String::from("hello");
a;
// a离开作用域,值被抛弃
}
// 错误!无法再次使用 a
// println!("{}", a);
而函数体里也是一个作用域
fn main() {
let a = String::from("hello");
// 这里实现了所有权的交接
let new_owner = take_ownership(a);
// 错误!无法再次使用 a
// println!("{}", a);
println!("{}", new_owner);
// 输出:hello
}
fn take_ownership(some_str: String) -> String {
return some_str;
// some_str离开作用域
}
引用
引用可分为:可变引用和不可变引用
可变引用:&mut type
不可变引用:&type
其中:
&
符号就是 引用,它们允许你使用值但不获取其所有权
type
指类型名,如:i32
, u32
, String
…
一个可变变量不能同时存在多个可变引用
这个限制的好处是 Rust 可以在编译时就避免数据竞争。数据竞争(data race)类似于竞态条件,它可由这三个行为造成:
- 两个或更多指针同时访问同一数据。
- 至少有一个指针被用来写入数据。
- 没有同步数据访问的机制。
let mut my_str = String::from("A mutable string");
let first_mutable_reference = &mut my_str;
let second_mutable_reference = &mut my_str;
// 注意:当两个可变引用都没有被使用时,可通过编译
// 即:此时将下面的代码注释掉,可通过编译
println!("{}", first_mutable_reference);
/*
error[E0499]: cannot borrow `my_str` as mutable more than once at a time
--> src\main.rs:5:36
|
4 | let first_mutable_reference = &mut my_str;
| ----------- first mutable borrow occurs here
5 | let second_mutable_reference = &mut my_str;
| ^^^^^^^^^^^ second mutable borrow occurs here
6 | println!("{}", first_mutable_reference);
| ----------------------- first borrow later used here
*/
一个可变变量不能同时存在多个不可变引用
既然是不可变的,那么创建多少个都不会引发安全问题
let mut my_str = String::from("A mutable string");
let first_unmutable_reference = &my_str;
let second_unmutable_reference = &my_str;
println!("{} {}", first_unmutable_reference, second_unmutable_reference);
//输出:A mutable string A mutable string
一个可变变量不能同时存在可变引用和不可变引用
因为当你创建不可变引用时,你并不希望指向的量在还没有被使用时就发生改变。
let mut my_str = String::from("A mutable string");
let unmutable_reference = &my_str;
let mutable_reference = &mut my_str;
// 注意:当两个引用都没有被使用时,可通过编译
// 即:此时将下面的代码注释掉,可通过编译
println!("{}", unmutable_reference);
/*
error[E0502]: cannot borrow `my_str` as mutable because it is also borrowed as immutable
--> src\main.rs:5:29
|
4 | let unmutable_reference = &my_str;
| ------- immutable borrow occurs here
5 | let mutable_reference = &mut my_str;
| ^^^^^^^^^^^ mutable borrow occurs here
6 | println!("{}", unmutable_reference);
| ------------------- immutable borrow later used here
*/
而当不可变引用第一次被使用后,默认其作用域已结束,此时可以创建可变引用。
let mut my_str = String::from("A mutable string");
let unmutable_reference = &my_str;
println!("{}", unmutable_reference);
let mutable_reference = &mut my_str;
println!{"{}", mutable_reference};
// 输出:
// A mutable string
// A mutable string
借用
如在所有权中所讲的那样,当变量被传入函数体(或者说一个新的作用域)中时,变量的所用权也从原作用域转移到新的作用域中。而要在新的作用域中变量,不一定要将变量的所有权交给新的作用域,就像生活中所做的那样,我们可以将变量借出去,这就是借用
借用就是向新的作用域传递变量的引用
let let mut my_str = String::from("A mutable string");
{
// 借用,所有权没有发生转移
let borrow = &my_str;
// borrow离开作用域
}
println!("{}", my_str);
// 输出:A mutable string
对于函数也是同理
fn main() {
let a = String::from("hello");
// 这里传入a的引用,没有交出所有权
borrow(&a);
println!("{}", a);
// 输出:
// borrow a string: hello
// hello
}
// 借用,所有权没有转移
fn borrow(some_str: &String) {
println!("borrow a string: {}", some_str);
}
Rust是基于表达式的编程语言,有且仅有两种语句 (statement):
声明语句 (declaration statement),比如进行变量绑定的let语句。
表达式语句 (expression statement),它通过在末尾加上分号;来将表达式变成语句, 丢弃该表达式的值,一律返回unit()
let my_str = "Hi".to_string();
my_str; // my_str被抛弃
// 下面的代码无法通过编译
// | println!("{}", my_str);
// | ^^^^^^ value borrowed here after move
// println!("{}", my_str);
slices
字符串 slice(string slice)是 String
中一部分值的引用
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
String Slices 的结构
fn main() {
let mut my_str = String::from("A mutable string");
let first_word = get_first_word(&my_str);
// first_word 是 my_str 的部分不可变引用,
// 而此时 first_word 尚未被使用, 所以其作用域尚未结束, 意味着 my_str 不可更改
// 下面一行的代码无法通过编译
// my_str.clear();
println!("{}", first_word);
// 输出: A
// 而此时 my_str 可更改
my_str.clear();
}
// 如果有一个字符串 slice,可以直接传递它, 如果有一个 String,则可以传递整个 String 的 slice
// 定义一个获取字符串 slice 而不是 String 引用的函数使得我们的 API 更加通用并且不会丢失任何功能
fn get_first_word(some_str: &str) -> &str {
let bytes = some_str.as_bytes();
// 因为从 .iter().enumerate() 中获取的是集合元素的引用,所以模式中使用了 &。
for (i, &item) in bytes.iter().enumerate() {
if item == b' '{
// 字符串 slice(string slice)是 String 中一部分值的 引用
// 字符串字面值就是 slice
// 它是一个指向二进制程序特定位置的 slice
// 有更通用的 slice 类型
// 如: &[i32]
// 这里的切片与python中的切片类似
// 只是不能有负索引
return &some_str[..i];
}
}
&some_str
}
也有更通用的 slice 类型,如:&[i32],它跟字符串 slice 的工作方式一样,通过存储第一个集合元素的引用和一个集合总长度。