From e0e52bc7d08e88b3fd8b68169a9ebd96cb45974f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Thu, 17 Mar 2022 16:50:51 +0800 Subject: [PATCH] =?UTF-8?q?enhance:=E5=9C=A8Rust=E6=89=80=E6=9C=89?= =?UTF-8?q?=E6=9D=83=E6=96=87=E7=AB=A0=E4=B8=AD=E5=A2=9E=E5=8A=A0=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/_posts/rust-ownership.md | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/source/_posts/rust-ownership.md b/source/_posts/rust-ownership.md index b10fad2..60b9251 100644 --- a/source/_posts/rust-ownership.md +++ b/source/_posts/rust-ownership.md @@ -428,6 +428,66 @@ fn start_sort_thread(mut persons: Vec, score: Score) -> thread::JoinHand 虽然被强行转移的引用并不会在闭包结束后被转移回来,但是如果在闭包之后还是需要使用被转移的引用内容,可以事先把会被转移的内容保存一个副本,然后将闭包限制在新副本中。 +## 内部可变性 + +因为Rust严格控制了所有权与借用关系,所以就使得编程过程中灵活性的丧失,一些对灵活性比较高的需求就无法满足了。所以为了增强灵活性,Rust在标准库中提供了`Cell`和`RefCell`。`Cell`和`RefCell`通过提供内部可变性来满足了对于灵活性的需求。 + +在一般情况下,如果需要修改一个对象,就必须采用以下两种方式之一: + +1. 获得对象的所有权,并且明确使用`mut`声明。 +1. 以`&mut`的方式借用对象。 + +但是通过使用`Cell`,就可以在需要的时候修改其中包装的对象。 + +!!! caution "" + `Cell`在使用的时候,要求`T`并须实现了`Copy`特型。但是`RefCell`不要求`T`实现`Copy`特型。所以在不确定`T`是否实现了`Copy`特型的时候,要优先选择`RefCell`。 + +`Cell`和`RefCell`主要提供了以下方法来进行被包装值的修改操作。 + +- `.get()`,返回被包装的值的一个副本。 +- `.set()`,更新被包装的值。 +- `.borrow()`,不可变借用被包装的值,可以同时存在多个不可变借用。 +- `.borrow_mut()`,可修改借用被包装的值,但是同时只能存在一个可修改借用。对于`RefCell`来说,在使用的时候必须严格注意程序中是否产生了多个可修改借用,多个可修改借用的存在会直接让程序产生诧异。 +- `.into_inner()`,取出被包装的值。 + +例如在程序中可以这样解决循环引用的问题。 + +```rust +struct Owner { + name: String, + delivers: RefCell>>, // Weak指针不能保证其引用的对象一定存在,可以通过其中的upgrade()方法来判断和获取。 +} + +struct Thing { + id: u32, + owner: Rc, +} + +// 在实际程序中可以如下来操作这两个结构体 +fn main() { + let owner1: Rc = Rc::new( + Owner { + name: "Deliver 1".to_string(), + delivers: RefCell::new(Vec::new()), + } + ); + + let thing1 = Rc::new( + Thing { + id: 1, + owner: owner1.clone(), + } + ); + + // Rc可以通过其中的downgrade()方法转换成Weak。 + // Weak可以通过其中的upgrade()方法转换成Option>。 + owner1.delivers.borrow_mut().push(Rc::downgrade(&thing1)); +} +``` + +!!! caution "" + `RefCell`不是线程安全的,通常只是与`Rc`配合使用在单线程内部解决循环引用的问题。 + ## 总结 其实对于Rust中的所有权机制的理解并不难,如果将Rust中所有的内存区域内容都看作是可以被消耗的资源的话,那么就可以比较容易的理解所有权机制了。如果一个资源被从一个处理过程转交给了另一个处理过程,那么就是所有权的转移;如果一个资源被转交给了另一个处理过程,但是资源在被使用完毕以后又转交类回来,就可以被理解为资源的借用。但是如果一个资源发生了变化或者是被复制出了一个一模一样的资源,那么就是资源的消耗和复制过程了。