【Rust中级教程】1.10. 引用及内部可变性(简单回顾):引用、内部可变性、 ...

打印 上一主题 下一主题

主题 971|帖子 971|积分 2913

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
这篇文章只对全部权举行简单回顾,想要看完备的全部权系统叙述见【Rust自学】专栏的第15章的文章。

1.10.1. 引用

通过引用,Rust允许将值借用出去,但不放弃全部权。
引用就是带有附加合约的指针。Rust中一共有两种引用类型。
1. 共享的引用

共享的引用,又叫不可变的引用,Rust中写作&T,其中T指代类型。
它的特点在一可以同时(大概叫在同一作用域内)存在恣意数量的引用指向同一个值。每个共享的引用都实现了Copy trait。
共享引用背后的值不可变。编译器允许假定共享引用指向的值,在该引用存货期间是不会改变的。
举个例子:一个共享引用的值在某函数内被多次读取,那编译器就有权让其只读取一次,然后重用读取的值。
2. 可变引用

与不可变引用相对的就是可变引用,在Rust中写作&mut T。
可变引用是独占的,意味着在一个作用域内只能有一个可变引用,不能出现第二个可变引用或恣意数量的共享引用。所以不可变引用没有实现Copy trait。
编译器会假定没有其它线程访问可变引用所指向的类型(无论是通过共享引用照旧可变引用)。
1.10.2. 拥有值 vs. 拥有到值的可变引用

全部者必要对删除值(丢弃值)负责,除此之外两者的作用基本一样。
注意:如果你移动了可变引用背后的值,则必须在其位置上留下另一个值。如果不这样做,全部者会认为它必要将其删除(丢弃),但其实却没有值可以删除了,导致未定义行为编译错误
看个例子:
  1. fn main() {
  2.     let mut s = String::from("Hello");
  3.     let r = &mut s;
  4.     let t = *r;  // 试图移动 r 所指向的值
  5.     println!("{}", r);  // r 变成了悬垂引用
  6. }
复制代码
输出:
  1. error[E0507]: cannot move out of `*r` which is behind a mutable reference
复制代码
我们来梳理一下过程:


  • r是 s的可变引用,而 *r的操作试图移动这个值(String类型没有实现Copy trait,意味着s会失去数据)
  • 由于s仍旧存在,当s作用域结束时,Rust期望可以正常开释它的内存
  • 但s已经被移动走了,导致Rust不知道该怎样正确开释它,从而引发编译错误
正确的做法:
  1. fn main() {
  2.     let mut s = String::from("Hello");
  3.     let r = &mut s;
  4.     let t = std::mem::replace(r, String::new()); // 用空字符串替换原值
  5.     println!("{}", t);  // "Hello"
  6.     println!("{}", s);  // ""
  7. }
复制代码
1.10.3. 内部可变性

一些类型提供了内部可变性,这些类型可以通过共享引用修改值
这些类型通常依赖于额外的机制(如原子CPU指令)或稳定量来提供安全的可变形,而不依赖于独占引用的语义。
内部可变性分为两类:


  • 通过共享引用得到可变引用:Mutex、RefCell(这两者在【Rust自学】专栏的第15章的文章中都介绍过)
    这类类型提供了保障机制——如果对某个值提供了可变引用,那么同时(大概叫在同一作用域下)只会存在一个可变引用,而且没有共享引用。这种功能依赖于UnsafeCell类型,通过共享引用修改值的唯一正确方式。
  • 通过共享引用可以更换值:std::sync::atomic、std::cell::Cell
    这类类型没有提供可变引用到内部的值,但是提供了就地操作值的方法——比如说更换/读取一个值。例如:无法得到到usize或i32的直接引用,但是可以读取和更换值。
1.10.4. Cell类型

Cell类型来自于尺度库,它通过稳定量实现内部可变性。


  • Cell类型无法跨线程共享,由于内部值不会被并发地修改,即使通过共享引用发生修改
  • 不会提供到Cell内部的值的引用(所以可以一直移动它)
Cell提供的方法:


  • 对值整体更换(也就是所谓的就地操作)
  • 返回值的副本(也就是读取)
1. set(value): 更换值

  1. use std::cell::Cell;
  2. fn main() {
  3.     let x = Cell::new(10);  // 创建一个 `Cell`,存储 10
  4.     x.set(20);  // 替换内部值
  5.     println!("Updated value: {}", x.get()); // 输出 20
  6. }
复制代码


  • set(value)用新值更换Cell内部的值
2. get():返回值的副本

  1. use std::cell::Cell;
  2. fn main() {
  3.     let x = Cell::new(5);
  4.     let y = x.get(); // 获取 `x` 内部的副本
  5.     println!("Value: {}", y); // 输出 5
  6. }
复制代码


  • get()不会返回内部值的引用,而是返回值的副本(实用于实现Copy trait 的类型)。
  • 实用于i32、bool等实现Copy trait 的类型

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

写过一篇

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表