- Hands-On Data Structures and Algorithms with Rust
- Claus Matzinger
- 453字
- 2021-07-02 14:11:42
Multiple owners
As powerful as single ownership is, it does not work for every use case. Large objects or shared objects that other instances need to own are examples where immutable ownership makes life easier. Consider a function that requires an owned object to be passed in:
#[derive(Debug)]
struct FileName {
name: String,
ext: String
}
fn no_ref_counter() {
let name = String::from("main");
let ext = String::from("rs");
for _ in 0..3 {
println!("{;?}", FileName {
name: name,
ext: ext
});
}
}
When trying to compile no_ref_counter(), the compiler creates a scope for each iteration of the loop and owns any value that is used within it. This works exactly once, since afterward, the variable has been moved and is inaccessible for subsequent iterations.
Consequently, these values (in this case, name and ext) are gone and compilation will yield two errors, one for each "second" move of a string:
error[E0382]: use of moved value: `name`
--> src/main.rs:63:33
|
63 | let _ = FileName { name: name, ext: ext };
| ^^^^ value moved here in previous iteration of loop
|
= note: move occurs because `name` has type `std::string::String`, which does not implement the `Copy` trait
error[E0382]: use of moved value: `ext`
--> src/main.rs:63:44
|
63 | let _ = FileName { name: name, ext: ext };
| ^^^ value moved here in previous iteration of loop
|
= note: move occurs because `ext` has type `std::string::String`, which does not implement the `Copy` trait
One solution is to clone the object in every iteration, but that causes a lot of slow memory allocations. For this, the Rust standard library provides a solution: reference counting.
A reference counter (std::rc::Rc<T>) encapsulates a variable of type T allocated on the heap and returns an immutable reference when created. This reference can be cloned with low overhead (it's only a reference count that is incremented) but never transformed into a mutable reference. Regardless, it acts just like owned data, passing through function calls and property lookups.
While this requires a change to the variable types, a call to clone() is now far cheaper than cloning the data directly:
use std::rc::Rc;
#[derive(Debug)]
struct FileName {
name: Rc<String>,
ext: Rc<String>
}
fn ref_counter() {
let name = Rc::new(String::from("main"));
let ext = Rc::new(String::from("rs")));
for _ in 0..3 {
println!("{;?}", FileName {
name: name.clone(),
ext: ext.clone()
});
}
}
Running this snippet prints the debug version of the FileName object three times:
FileName { name: "main", ext: "rs" }
FileName { name: "main", ext: "rs" }
FileName { name: "main", ext: "rs" }
This approach works great for single-threaded and immutable scenarios, but will refuse to compile multithreaded code. The solution to this will be discussed in the next section.
- SQL Server 2016 數據庫教程(第4版)
- Python絕技:運用Python成為頂級數據工程師
- 云計算環境下的信息資源集成與服務
- 信息系統與數據科學
- Access 2007數據庫應用上機指導與練習
- Hadoop大數據實戰權威指南(第2版)
- Creating Dynamic UIs with Android Fragments(Second Edition)
- Mockito Cookbook
- Oracle PL/SQL實例精解(原書第5版)
- 圖數據實戰:用圖思維和圖技術解決復雜問題
- Python數據分析與挖掘實戰(第3版)
- SQL Server 2012實施與管理實戰指南
- Unreal Engine Virtual Reality Quick Start Guide
- 貫通SQL Server 2008數據庫系統開發
- Gideros Mobile Game Development