RustPrimer
  • Introduction
  • 初识Rust
  • 安装Rust
    • Linux
    • Mac
    • Windows
    • 版本管理工具: rustup
  • 编辑器
    • 前期准备
    • vim
    • emacs
    • vscode
    • atom
    • sublime
    • visual studio
    • spacemacs
  • Rust快速入门
    • Rust旅程
    • 变量绑定与原生类型
    • 数组、动态数组和字符串
    • 结构体与枚举
    • 控制流
    • 函数与方法
    • 特性
    • 注释与文档
    • 输入输出流
  • Cargo项目管理器
  • 基本程序结构
    • 注释
    • 条件
    • 循环
  • 类型、运算符和字符串
    • 基础类型
    • 复合类型
    • 字符串类
    • 基础运算符和字符串格式化
  • 函数
    • 函数参数
    • 函数返回值
    • 语句和表达式
    • 高阶函数
  • 模式匹配
    • match关键字
    • 模式 pattern
  • 特征 Trait
    • trait关键字
    • trait对象
  • 泛型
  • 可变性、所有权、租借和生命期
    • 所有权
    • 引用和借用
    • 生命周期
  • 闭包
    • 闭包的语法
    • 闭包的实现
    • 闭包作为参数和返回值
  • 集合类型 Collections
    • 动态数组 Vec
    • 哈希表 HashMap
  • 迭代器
    • 迭代器、适配器、消费者
  • 模块和包系统、Prelude
    • 模块 module 和包 crate
    • Prelude
  • Option、Result与错误处理
  • 输入与输出
  • 宏系统
  • 堆、栈与Box
  • 几种智能指针
    • Rc, Arc
    • Mutex, RwLock
    • Cell, RefCell
  • 类型系统中的几个常见 Trait
    • Into/From 及其在 String 和 &str 互转上的应用
    • AsRef, AsMut
    • Borrow, BorrowMut, ToOwned
    • Deref 与 Deref coercions
    • Cow 及其在 String 和 &str 上的应用
  • Send 和 Sync
  • 并发,并行,多线程编程
    • 线程
    • 消息传递
    • 共享内存
    • 同步
    • 并行
  • Unsafe、原始指针
    • Unsafe
    • 原始指针
  • FFI
    • rust调用ffi函数
    • 将rust编译成库
  • 运算符重载
  • 属性和编译器参数
    • 属性
    • 编译器参数
  • Cargo参数配置
  • 测试与评测
    • 测试 (testing)
    • 评测 (benchmark)
  • 代码风格
  • Any与反射
  • 安全(safe)
  • 常用数据结构实现
    • 栈结构
    • 队列
    • 二叉树
    • 优先队列
    • 链表
    • 图结构
  • 标准库介绍
    • 系统命令:调用grep
    • 目录操作:简单grep
    • 网络模块:W回音
  • 实战篇
    • 实战:Json处理
    • 实战:Web 应用开发入门
    • 实战:使用Postgresql数据库
  • 附录-术语表
Powered by GitBook
On this page
  • 借用与引用的区别
  • 规则
  • 可变性
  • 高级例子
  • 总结

Was this helpful?

  1. 可变性、所有权、租借和生命期

引用和借用

如上所示,Owership让我们改变一个变量的值变得“复杂”,那能否像其他编程语言那样随意改变变量的值呢?答案是有的。

所有权系统允许我们通过“Borrowing”的方式达到这个目的。这个机制非常像其他编程语言中的“读写锁”,即同一时刻,只能拥有一个“写锁”,或只能拥有多个“读锁”,不允许“写锁”和“读锁”在同一时刻同时出现。当然这也是数据读写过程中保障一致性的典型做法。只不过Rust是在编译中完成这个(Borrowing)检查的,而不是在运行时,这也就是为什么其他语言程序在运行过程中,容易出现死锁或者野指针的问题。

通过&符号完成Borrowing:

fn main() {
    let x: Vec<i32> = vec!(1i32, 2, 3);
    let y = &x;
    println!("x={:?}, y={:?}", x, y);
}

Borrowing(&x)并不会发生所有权moved,所以println可以同时访问x和y。 通过引用,就可以对普通类型完成修改。

fn main() {
    let mut x: i32 = 100;
    {
        let y: &mut i32 = &mut x;
        *y += 2;
    }
    println!("{}", x);
}

借用与引用的区别

借用与引用是一种相辅相成的关系,若B是对A的引用,也可称之为B借用了A。

很相近对吧,但是借用一词本意为要归还。所以在Rust用引用时,一定要注意应该在何处何时正确的“归回”借用/引用。 最后面的“高级”小节会详细举例。

规则

  1. 同一时刻,最多只有一个可变借用(&mut T),或者2。

  2. 同一时刻,可有0个或多个不可变借用(&T)但不能有任何可变借用。

  3. 借用在离开作用域后释放。

  4. 在可变借用释放前不可访问源变量。

可变性

Borrowing也分“不可变借用”(默认,&T)和“可变借用”(&mut T)。

顾名思义,“不可变借用”是只读的,不可更新被引用的内容。

fn main() {
    let x: Vec<i32> = vec!(1i32, 2, 3);

    //可同时有多个不可变借用
    let y = &x;
    let z = &x;
    let m = &x;

    //ok
    println!("{:?}, {:?}, {:?}, {:?}", x, y, z, m);
}

再次强调下,同一时刻只能有一个可变借用(&mut T),且被借用的变量本身必须有可变性 :

fn main() {
    //源变量x可变性
    let mut x: Vec<i32> = vec!(1i32, 2, 3);

    //只能有一个可变借用
    let y = &mut x;
    // let z = &mut x; //错误
    y.push(100);

    //ok
    println!("{:?}", y);

    //错误,可变借用未释放,源变量不可访问
    // println!("{:?}", x);
}  //y在此处销毁

高级例子

下面的复杂例子,进行了详细的注释,即使看不懂也没关系,可以在完成Lifetimes(生命周期)的学习后再仔细思考本例子。

fn main() {
    let mut x: Vec<i32> = vec!(1i32, 2, 3);

    //更新数组
    //push中对数组进行了可变借用,并在push函数退出时销毁这个借用
    x.push(10);

    {
        //可变借用1
        let mut y = &mut x;
        y.push(100);

        //可变借用2,注意:此处是对y的借用,不可再对x进行借用,
        //因为y在此时依然存活。
        let z = &mut y;
        z.push(1000);

        println!("{:?}", z); //打印: [1, 2, 3, 10, 100, 1000]
    } //y和z在此处被销毁,并释放借用。


    //访问x正常
    println!("{:?}", x); //打印: [1, 2, 3, 10, 100, 1000]
}

总结

  1. 借用不改变内存的所有者(Owner),借用只是对源内存的临时引用。

  2. 在借用周期内,借用方可以读写这块内存,所有者被禁止读写内存;且所有者保证在有“借用”存在的情况下,不会释放或转移内存。

  3. 失去所有权的变量不可以被借用(访问)。

  4. 在租借期内,内存所有者保证不会释放/转移/可变租借这块内存,但如果是在非可变租借的情况下,所有者是允许继续非可变租借出去的。

  5. 借用周期满后,所有者收回读写权限

  6. 借用周期小于被借用者(所有者)的生命周期。

备注: 借用周期,指的是借用的有效时间段。

Previous所有权Next生命周期

Last updated 5 years ago

Was this helpful?