原文:选自《Rust异步编程》第4章 Pinning
译者注:如果你一时半会没啃动Pinning,也别心急,试试阅读这篇《Rust的Pin与Unpin - Folyd》,理解起来会容易不少。
Pinning详解
让我们尝试使用一个比较简单的示例来了解pinning。前面我们遇到的问题,最终可以归结为如何在Rust中处理自引用类型的引用的问题。
现在,我们的示例如下所示:
use std::pin::Pin;
#[derive(Debug)]
struct Test {
a: String,
b: *const String,
}
impl Test {
fn new(txt: &str) -> Self {
Test {
a: String::from(txt),
b: std::ptr::null(),
}
}
fn init(&mut self) {
let self_ref: *const String = &self.a;
self.b = self_ref;
}
fn a(&self) -> &str {
&self.a
}
fn b(&self) -> &String {
unsafe {
&*(self.b)}
}
}
Test
提供了获取字段a和b值引用的方法。由于b是对a的引用,因此我们将其存储为指针,因为Rust的借用规则不允许我们定义这种生命周期。现在,我们有了所谓的自引用结构。
如果我们不移动任何数据,则该示例运行良好,可以通过运行示例观察:
fn main() {
let mut test1 = Test::new("test1");
test1.init();
let mut test2 = Test::new("test2");
test2.init();
println!("a: {}, b: {}", test1.a(), test1.b());
println!("a: {}, b: {}", test2.a(), test2.b());
}
我们得到了我们期望的结果:
a: test1, b: test1
a: test2, b: test2
让我们看看如果将test1
与test2
交换导致数据移动会发生什么:
fn main() {
let mut test1 = Test::new("test1");
test1.init();
let mut test2 = Test::new("test2");
test2.init();
println!("a: {}, b: {}", test1.a(), test1.b());
std::mem::swap(&mut test1, &mut test2);
println!(