Rust:所有权的理解

理解思路

1、所有权的存在是为了解决内存安全的问题,而产生的一种新的区别于其它语言与内存打交道(垃圾回收机制和手动管理内存)的方法--通过所有权来管理内存。

2、那么如何通过所有权管理内存呢?rust就规定每个值只能有一个所有者,且离开了作用域以后就会被drop.

复制代码
有了所有者,就存在管理权转移的可能,那么就有3、4描述的问题

3、无需在堆上分配空间的栈上数据,可以通过自动拷贝,不用发生所有权转移 ,比如整型数据这类基本数据类型

rust 复制代码
    //例如
    let x :i32=5;
    let y = x; //重新分配了内存空间绑定到了y
    println!("x :{},y: {}",x , y); //调用x不会产生错误

4、但是 String 不是基本类型,而且是存储在堆上的,因此不能自动拷贝。就会发生所有权转移

rust 复制代码
    let s1 = String::from("hello");
    let s2 = s1; // s1 失效,不再指向任何数据,s2拥有了s1的所有权
    println!("{}, world!", s1); // 产生错误
    

5、正因为有了以上规则,所以rust规定在将值传递给函数,一样会发生 移动 或者 复制

rust 复制代码
    fn main() {
        let s = String::from("hello");  // s 进入作用域

        takes_ownership(s);             // s 的值移动到函数里 ...
                                        // ... 所以到这里不再有效

        let x = 5;                      // x 进入作用域

        makes_copy(x);                  // x 应该移动函数里,
                                        // 但 i32 是 Copy 的,所以在后面可继续使用 x

    } // 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,
      // 所以不会有特殊操作

    fn takes_ownership(some_string: String) { // some_string 进入作用域
        println!("{}", some_string);
    } // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放

    fn makes_copy(some_integer: i32) { // some_integer 进入作用域
        println!("{}", some_integer);
    } // 这里,some_integer 移出作用域。不会有特殊操作

6、那么通过5点,就会产生一个新问题?s 如果要在takes_ownership()后使用怎么办?

rust 复制代码
    1、要么在函数前copy 一下: let s1 = s.clone(),这样就会重新分配内存保存一份同样的数据让s1拥有所有权,s的操作就与s1无关。
    2、或者函数返回s的值,调用函数后重新接收。

7、所以为了避免变量在调用函数时发生所有权转移,Rust 通过 借用(Borrowing) 这个概念来达成目的,获取变量的引用,称之为借用(borrowing)

ini 复制代码
   & 符号即是引用,它们允许你使用值,但是不获取所有权
   * 符号是解出引用所指向的值(也就是**解引用**)
   例如 let x = 5 ; y = &x; //这里的&x就是引用了x的值,*y解引用y所指向的x的值
   就可以避免通过函数参数传入所有权,然后再通过函数返回来传出所有权

8、由于存在不可变 和 可变变量,所以引用也是存在 不可变引用 和 可变引用

rust 复制代码
    fn main() {
        let s = String::from("hello");
        change(&s);
    }

    fn change(some_string: &String) {
        some_string.push_str(", world");
    }
    //想改变s的值,这里就会报错,因为 s 是不可变变量,所以引用以后也不能改变其值
   

因此 s 需要被定义为 let mut s = String::from("hello"); 可变变量才行,然后change的引用才是可变引用;同时change方法也要发生改变

rust 复制代码
    //同时change方法也要发生改变
    fn change(some_string: &mut String) { 
        some_string.push_str(", world"); 
    }

不过可变引用并不是随心所欲、想用就用的,它有一个很大的限制: **同一作用域,特定数据只能有一个可变引用**

9、有一个神奇的地方,可变变量,可以使用不可变引用,但是可变引用与不可变引用不能同时存在

rust 复制代码
    let mut s = String::from("hello");

    let r1 = &s; // 没问题
    let r2 = &s; // 没问题
    let r3 = &mut s; // 不行,会产生错误

    println!("{}, {}, and {}", r1, r2, r3); //这里不会像其它语言一样,r1、r2输出和r3可能改变以后的值,只会报错。

10、悬垂引用(Dangling References)

悬垂引用也叫做悬垂指针,意思为指针指向某个值后,这个值被释放掉了,而指针仍然存在,其指向的内存可能不存在任何值或已被其它变量重新使用。在 Rust 中编译器可以确保引用永远也不会变成悬垂状态:当你获取数据的引用后,编译器可以确保数据不会在引用结束前被释放,要想释放数据,必须先停止其引用的使用。

rust 复制代码
    fn main() {
        let reference_to_nothing = dangle();
    }

    fn dangle() -> &String {
        let s = String::from("hello");

        &s // 返回字符串 s 的引用
    }// 这里 s 离开作用域并被丢弃。其内存被释放。所以不能返回指针 ,解决方法直接返回 s ,将s的所有权转移给外部调用者使用

所以这算是Rust学习曲线陡峭的地方吗?理顺了就觉得还好!feeling good!

相关推荐
love530love4 小时前
【笔记】在 MSYS2(MINGW64)中正确安装 Rust
运维·开发语言·人工智能·windows·笔记·python·rust
景天科技苑10 小时前
【Rust宏编程】Rust有关宏编程底层原理解析与应用实战
开发语言·后端·rust·rust宏·宏编程·rust宏编程
维维酱12 小时前
Rust - 消息传递
rust
Kapaseker16 小时前
Android程序员初学Rust-线程
rust
猩猩程序员17 小时前
十年下注 Rust,我期待的下一个十年
rust
Humbunklung1 天前
Rust 控制流
开发语言·算法·rust
UestcXiye2 天前
Rust 学习笔记:Box<T>
rust
Kapaseker2 天前
Android程序员初学Rust-错误处理
rust
用户27692024453462 天前
基于 Tauri + Vue3 的现代化新流串口调试助手 v2
前端·rust