所有权和生命周期

9 篇文章 1 订阅
6 篇文章 0 订阅
本文详细解析了Rust语言中的所有权概念,包括基本类型所有权、结构体类型所有权、参数所有权,以及生命周期注释的必要性。通过实例展示了Copy与借用的区别,以及如何处理不Copy情况下的生命周期管理,适用于 Rust 开发者进阶学习。
摘要由CSDN通过智能技术生成


提示

所有帖子都会不定期添加补充实战过程中发现的新内容,力争一篇文章涵盖所有内容,避免你多次查找文章,所以最好点赞或收藏,时不时回来看看。


废话

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.对于传递多个相同类型的变量并经过比较或计算返回其中一个时,需要使用参数的生命周期注解。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值