提示
所有帖子都会不定期添加补充实战过程中发现的新内容,力争一篇文章涵盖所有内容,避免你多次查找文章,所以最好点赞或收藏,时不时回来看看。
废话
rust语言的一个特点是所有权,用所有权来处理超出作用域的变量释放,这样就不需要GC(垃圾回收)机制了,即变量的生命周期。
1.Rust 中的每一个值都有一个被称为其 所有者(owner)的变量。
2.值在任一时刻有且只有一个所有者。
3.当所有者(变量)离开作用域,这个值将被丢弃。
基本类型所有权
fn main() {
let a = 2i32;
let b = a;
println!("b is {}", b);
println!("a is {}", a);
}
执行结果:
b is 2
a is 2
结构体类型所有权
struct Person {
age: u8,
}
fn main() {
let a = Person{ age: 16, };
let b = a;
println!("b.age is {}", b.age);
println!("a.age is {}", a.age);
}
意料之外的执行结果:
println!("a.age is {}", a.age);
| ^^^^^ value borrowed here after move
说借用a时a已经被move掉了,原因是b = a时,a的所有权转移到了b,a失去所有权后被释放了,所以不能再使用a了。
解决办法也简单,给Person结构体实现Copy特性就可以了
#[derive(Copy, Clone)]
struct Person {
age: u8,
}
fn main() {
let a = Person{ age: 16, };
let b = a;
println!("b.age is {}", b.age);
println!("a.age is {}", a.age);
}
执行结果:
b.age is 16
a.age is 16
为什么基本类型就可以正常编译呢,因为rust所有整数、浮点数、布尔、字符char、元组(当且仅当其包含的类型也都是 Copy 的时候)等基本类型都默认支持Copy特性,Copy的意思是复制一个一模一样的,也就是说a和b是2个对象,测试一下
#[derive(Copy, Clone)]
struct Person {
age: u8,
}
fn main() {
let mut a = Person{ age: 16, };
let b = a;
a.age = 24;
println!("b.age is {}", b.age);
println!("a.age is {}", a.age);
}
执行结果:
b.age is 16
a.age is 24
给结构体增加个字符串类型的字段试试
#[derive(Copy, Clone)]
struct Person {
age: u8,
name: String,
}
fn main() {
let mut a = Person{ age: 16, name: String::from("张三")};
let b = a;
a.age = 24;
println!("b.age is {}", b.age);
println!("a.age is {}", a.age);
}
运行出错,提示:
name: String,
------------ this field does not implement `Copy`
完蛋,编译又在教我做事了,说String类型没有实现Copy,这好办,我自己实现一下就是了,但自己实现就不能在结构体前面声明了,
struct Person {
age: u8,
name: String,
}
// 自己实现下Clone特性
impl Clone for Person {
fn clone(&self) -> Self {
Self {
age: self.age,
name: self.name.clone(), // 重点是这里
}
}
}
fn main() {
let a = Person{ age: 16, name: String::from("张三"), };
println!("a.name is {}", a.name);
let mut b = a;
b.age = 24;
b.name = String::from("李四");
println!("b.name is {}", b.name);
}
运行结果:
a.name is 张三
b.name is 李四
参数所有权
把变量传递给函数或方法的参数后,也会转换所有权,如下
struct Person {
age: u8,
}
fn set_age(mut per: Person, val: u8) {
per.age = val;
}
fn main() {
let a = Person{ age: 16, };
let n = 24;
set_age(a, n);
println!("a.age is {}", a.age);
}
运行出错,提示:
println!("a.age is {}", a.age);
^^^^^ value borrowed here after move
解决办法同上,给Person增加Copy特性即可,这里就不再祭上代码了。
不Copy解决上面的问题
通过copy,会复制一个一模一样的变量,会占用内存。对于函数或方法有时候需要将参数传递进去,里面要处理变量的值并返回到原调用处的变量,对于这种情况可以使用借用或可变借用
struct Person {
age: u8,
}
fn set_age(per: &mut Person, val: u8) {
per.age = val;
}
fn main() {
let mut a = Person{ age: 16, };
let n = 24;
set_age(&mut a, n);
println!("a.age is {}", a.age);
}
运行结果:
a.age is 24
注意:如果mut修饰的是变量名,那么它代表这个变量可以被重新绑定; 如果mut修饰的是“借用指针&”,借用指针(borrow pointer)也可以称作“引 用”(reference)。借用指针与普通指针的内部数据是一模一样的,唯一 的区别是语义层面上的。它的作用是告诉编译器,它对指向的这块内存 区域没有所有权。
生命周期注释
我想比较2个人,得到年龄最大的,写了下面的代码,但是无法编译通过,
struct Person {
age: u8,
}
fn max_age(per1: &Person, per2: &Person) -> &Person {
if per1.age > per2.age {
per1
} else {
per2
}
}
fn main() {
let per1 = Person{ age: 16, };
let per2 = Person{ age: 26, };
let per = max_age(&per1, &per2);
println!("max age is {}", per.age);
}
max_age拥有的引用是否有效呢?答案是不确定。因为如果max_age的返回per1则per2是无效的,如果返回per2则per1是无效的。也就是说,这个函数无法确定输入和输出的生命周期的关系。像这种函数须进行生命周期注解,或叫生命周期注释
struct Person {
age: u8,
}
fn max_age<'a>(per1: &'a Person, per2: &'a Person) -> &'a Person {
if per1.age > per2.age {
per1
} else {
per2
}
}
fn main() {
let per1 = Person{ age: 16, };
let per2 = Person{ age: 26, };
let per = max_age(&per1, &per2);
println!("max age is {}", per.age);
}
运行结果:
max age is 26
'a是一个生命周期注解,生命周期注解并不改变任何引用的生命周期的长短。生命周期的作用就是告诉 Rust 多个生命周期参数如何相互联系。一个生命周期为’a的参数per1,一个生命周期也为’a的参数per2,那么就说明per1与per2的生命周期是一样长的,同时返回值的生命周期和per1或per2是一致的。
总结
1.对于变量传递时,因为Copy会复制一个变量,并占用内存,所以对于那种对传入参数做访问或修改后返回到上下文继续使用的,使用借用或可变借用,防止重复创建耗资源费时间,当然明白了这个原理后,对于防止传递后修改了参数字段的情况则可以直接传递。
2.对于传递多个相同类型的变量并经过比较或计算返回其中一个时,需要使用参数的生命周期注解。