所有权是Rust最独特的功能,其令Rust无需垃圾回收即可保障内存安全
所有权与函数传参
fn main() {
let s = String::from("Hello world"); // s 进入作用域
takes_ownership(s); // 所有权系统将s的值交给s_str,然后s就失效了,此后不能在访问s
// println!("{}", s); //error[E0382]: borrow of moved value: `s`
//String是堆上的数据,因此有所有权系统管理。
let x = 5; // x 进入作用域
makes_copy(x); // i32 是 Copy 的,所以在后面可继续使用 x:xx将x绑定的值复制一份,并且绑定到s_int变量中
println!("{}", x);
} //这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,所以不会有特殊操作
fn takes_ownership(s_str:String){// some_string 进入作用域
println!("{}", s_str);
} // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放
fn makes_copy(s_int:i32){ // some_integer 进入作用域
println!("{}", s_int);
} // 这里,some_integer 移出作用域。不会有特殊操作
总结:在堆上的数据由所有权系统管理,函数传参时所有权系统会将值的所有权交给被调用函数的形参,此时,原先绑定值变量失去了自己的值,就失效了
在栈上的数据传值时是传副本,原先绑定值变量并没有失去自己的值,依然可以被访问
不可变变量和不可变引用
1、不可变变量不可以修改所绑定的值,不可变引用只能向第一个人借值,如果借到了就不能再向其他人借值了。
fn main() {
let s1 = String::from("Hello"); //s1进入作用域
let s = &s1; //引用s1:创建一个栈空间s,指向s1,也就是s里面存储s1的地址
let len = calculate_length(s); //s是copy的。将s的值也就是s1的地址拷贝一份,并且绑定到 calculate_length的形参s上
println!("the length of '{}' is {}", s1, len); //the length of 'Hello' is 5
println!("{}", s) //Hello
}
fn calculate_length(s:&String)->usize{ //s是对String的引用
// s.push_str(", world"); error //因为s实质是对不可变变量的引用,因此不允许改变其值
s.len()
} //当形参s离开当前作用域时,它绑定的值也就是String的地址被丢弃,但是不影响实际的String,其有有权没有被变更过
我们将获取引用作为参数参数称为借用。 正如现实生活中,如果一个人拥有某样东西,你可以从他那里借来。当你使用完毕,必须还回去。
默认不允许修改借来的值
规则:默认不允许修改引用的值
fn main() {
let s1 = String::from("Hello");
change(&s1);
println!("s1 = {} ", s1);
}
fn change(s:& String){
// s.push_str(" world") error //因为s实质是对不可变变量的引用,因此不允许改变其值
println!("s = {} ", s);
}
fn main() {
let s1 = String::from("Hello"); //s1不可变
let r = & s1;
let s2 = String::from("Hello");
// r = & s2; error:r是不可变的
}
不可变变量和不可变引用皆不可改变
2、不可变变量允许多个不可变引用同时借值,反正大家都不能改变这个值–>全部干看着吧
fn main() {
let s1 = String::from("Hello"); //s1不可变
let r1 = &s1;
let r2 = &s1;
println!("{}, {} ", r1, r2); //Hello
}
3、不可变变量可以借值给可变引用和不可变引用,但是不可变变量只有一种借值方法:就是不允许可变引用和不可变引用修改这个值。
可变变量和不可变引用
可变变量有两种借值方法:允许改变其值或者不允许改变其值.
不允许借值者修改值
fn main() {
let mut s1 = String::from("Hello"); //s1可变
s1.push_str(" rust");
let r1 = &s1;
nochange(r1);
println!("{}", r1); //Hello rust
}
fn nochange(r:&String){
// r.push_str("world"); //error[E0596]: cannot borrow `*r` as mutable, as it is behind a `&` reference
println!("{}", r) //Hello rust
}
给借值者修改值的权限
fn main() {
let mut s1 = String::from("Hello"); //s1可变
s1.push_str(" rust");
let r1 = &mut s1;
nochange(r1);
println!("{}", r1); //Hello rustworld
}
fn nochange(r:&mut String){
r.push_str("world");
println!("{}", r) //Hello rustworld
}
不可变变量与可变引用
可变引用可以多个人借钱,包括可变变量和不可变变量[任意顺序]
fn main() {
let s1 = String::from("Hello"); //s1不可变
let mut r = & s1; //r是可变的
println!("{} ", r); //Hello
let s2 = String::from("world");
r = & s2;
println!("{}", r); //"world"
}
一个可变引用向不可变变量借值,可变引用没有权利对这个值做出改变
fn main() {
let s1 = String::from("Hello"); //s1不可变
let mut r = & s1; //r是可变的: warning: variable does not need to be mutable。help: remove this `mut`
try_change(r);
println!("{} ", r); //Hello
}
fn try_change(r:&String){
println!("{}",r);
}
可变变量和可变引用
fn main() {
let mut s1 = String::from("Hello");
change(&mut s1);
println!("s1 = {} ", s1);
}
fn change(s:&mut String){
s.push_str(" world") ; //因为s实质是针对一块可变变量的引用
}
总结:mut给予了权限:允许你改变借来的东西
总结:
1、可变引用可以向多个人[变量]借钱[值],不可变引用只能向一个人[变量]借钱[值],当不可变引用第一次借到钱[值]之后,就不能在像其它[变量]人借钱[值]
可变引用可以向不可变变量和可变变量借值:
- 不可变变量–>可变引用:可变引用无权改变值,不可变变量不可以改变值。此时可变引用实质应该写成不可变引用,也就是应该为不可变变量–>不可变引用
- 可变变量->可变引用:可变引用有权改变其值,可变变量可以改变值
- 不可变变量–>不可变引用&可变变量->不可变引用:不可变引用可以向可变变量或者不可变变量借钱,反正都不能修改,但是不可变引用只允许向一个变量借钱,如果已经借到了,就不能像其他人借钱了。区别在于可变变量可以改变值,不可变变量不可以改变值
其他:可变变量的原则(。・∀・)ノ
编写一个函数,该函数接收一个字符串,并返回在该字符串中找到的第一个单词。如果没有找,返回空
fn main() {
let s = "string is composed of char".to_string();
//s.clear(); //cannot borrow `s` as mutable, as it is not declared as mutable:s是不可变的,因此不能调用这个方法,如果想要调用这个方法,必须let mut s
let res = first_word(&s); //获取 &String 而不是 String,没有得到值得所有权
println!("{}", res);
}
fn first_word(s:&String)->&str{
for (i, &item) in s.as_bytes().into_iter().enumerate(){
if item == b' '{ //空格是一个char,因此需要' '
return &s[0..=i]; //返回一个引用,这个引用指向传入得那一片内存得一小块[ptr相同但是len和cap不相同]
}
}
return ""
}
2、可变变量的值如果曾经以不可变权限借值出去的话,就不能再以可变权限借值出去
【第一次借值出去是不允许别人修改,那么这个值就是不能再让别人修改了】—>真有原则
当然,如果先前给予别人修改值得权限,再次借给别人得时候可以收回值得权限,但是已经收回值得权限了,就不能再给人修改值得权限了
fn main() {
let mut s = "string is composed of char".to_string();
s = "123 456".to_string(); //可以修改
let res = first_word(&s); //获取 &String 而不是 String,没有得到值得所有权
s = "123".to_string();//error[E0506]: cannot assign to `s` because it is borrowed:值被借出去了,还没有还回来,因此不允许修改
s.clear(); //cannot borrow `s` as mutable because it is also borrowed as immutable:说了不许修改了,以后
println!("{}", res);
}
fn first_word(s:&String)->&str{
for (i, &item) in s.as_bytes().into_iter().enumerate(){
if item == b' '{ //空格是一个char,因此需要' '
return &s[0..=i]; //返回一个引用,这个引用指向传入得那一片内存得一小块[ptr相同但是len和cap不相同]
}
}
return ""
}
总结:不能在相同作用域中同时存在可变和不可变引用的规则。。推测为什么要这么设计:
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
//不可变引用
let first = &v[0];
//可变引用
v.push(6); // cannot borrow `v` as mutable because it is also borrowed as immutable
println!("The first element is: {}", first);
}
vector的结尾增加新元素时,在没有足够空间将所有元素依次相邻存放的情况下,可能会要求分配新内存并将老的元素拷贝到新的空间中,然后释放旧空间。这时,第一个不可变引用就指向了被释放的内存,造成野指针
3、&str是一个指向二进制程序特定位置的slice。如果有一个String,可以传递整个String的slice。定义一个获取字符串slice而不是String引用的函数可以使API更加通用。
enumerate包装iter的结果并且返回一个元组,其中每一个元素是元组的一部分。enumerate返回元组的第一个元素是索引,第二个元素是集合中元素的引用。
fn main() {
let s = "string is composed of char".to_string();
let res = first_word(&s[..]); //获取 &String 而不是 String,没有得到值得所有权
println!("{}", res);
}
fn first_word(s:&str)->&str{
for (i, &item) in s.as_bytes().into_iter().enumerate(){
if item == b' '{ //空格是一个char,因此需要' '
return &s[0..=i]; //返回一个引用,这个引用指向传入得那一片内存得一小块[ptr相同但是len和cap不相同]
}
}
return ""
}
所有权与返回值
fn main() {
let s1 = gives_ownership(); //gives_ownership()返回一个值,这个值的所有权将转交给s1
let s2 = String::from("Hello");
let s3 = takes_and_gives_back(s2); //s2所绑定的值的的所有权转交给了形参a_string,此后s2失效,不可以再访问。s3获取到值得所有权
//println!("{}", s2); //error[E0382]: borrow of moved value: `s2`
println!("{}, {}", s1, s3);
} //s3移除作用域其值将通过drop被清理掉,s2已经被移出了因此什么操作也不会发生。s1移出作用域,其值将通过drop被清理掉
fn gives_ownership()->String{
let s_str = String::from("Hello"); //s_str进入作用域
s_str
}//将s_str的值转给调用的函数,s_str没有了绑定的值,s_str失效。
fn takes_and_gives_back(a_string:String)->String{ //a_string进入作用域
a_string //将a_string绑定的值转交给调用的函数
}
一块内存的所有者只能有一个
变量的所有权:将值赋给另一个变量时移动它。当持有堆中数据值得变量离开作用域时,其值将通过drop被清理掉,除非数据被移动为另一个变量所有。
参考:https://kaisery.github.io/trpl-zh-cn/ch04-01-what-is-ownership.html