在很少的情况下,我们必须调用一些数据成员的方法,这些方法拥有它们的所有权,但我们只有像&mut self后面的引用访问权.处理这个问题的一种模式是将它们包装在Option类型中,然后将它们包装在take()类型中以继续进行.

以下是该书中的一个例子:


struct Worker {
    id: usize,
    thread: Option<thread::JoinHandle<()>>,
}

pub struct ThreadPool {
    workers: Vec<Worker>,
    sender: Option<mpsc::Sender<Job>>,
}

impl Drop for ThreadPool {
    fn drop(&mut self) {

        drop(self.sender.take());

        for worker in &mut self.workers {
            if let Some(thread) = worker.thread.take() {
                thread.join().unwrap();
            }
        }
    }
}

通知threadsender被包装在Option中,只是为了在程序退出之前调用析构函数中的joindrop.然而,几乎他们的一生(除了在停止之前),他们将不会是None岁.这里使用的Option型是标准的feels more like a work around rather than utilizing its semantic that there could be a value there or not型.

这种模式在铁 rust 项目中普遍存在吗?或者有其他 Select 吗?我担心的是,在代码库中,我可能无法从这种看似专用的用法中快速分辨出"真正的可选用例",而且可能会强制执行大量的as_ref()unwrap()个调用.

推荐答案

如果你觉得Option很笨拙,另一种 Select 就是完全避免Drop.这将允许您从self开始"部分移动":

struct Worker {
    id: usize,
    thread: JoinHandle<()>,
}

pub struct ThreadPool {
    workers: Vec<Worker>,
    sender: Sender<Job>,
}

impl ThreadPool {
    pub fn wait(self) {
        drop(self.sender);
        for worker in self.workers {
            worker.thread.join().unwrap();
        }
    }
}

一种稍微复杂一些的替代方案是保留Drop,然后使用std::mem::replace()来获得&mut self中的自有值.因为Drop不允许部分移动,所以它需要将类型拆分成实现Drop的外部类型和不实现Drop的内部类型(但这种拆分在运行时是零成本的).

// definition of Worker unchanged from above

pub struct ThreadPool {
    inner: ThreadPoolInner,
}

struct ThreadPoolInner {
    workers: Vec<Worker>,
    sender: Sender<Job>,
}

impl ThreadPoolInner {
    fn wait(self) {
        drop(self.sender);
        for worker in self.workers {
            worker.thread.join().unwrap();
        }
    }
}

impl Drop for ThreadPool {
    fn drop(&mut self) {
        let empty = ThreadPoolInner {
            workers: vec![],
            sender: mpsc::channel::<Job>().0,
        };
        let owned_inner = std::mem::replace(&mut self.inner, empty);
        owned_inner.wait(); // or inline wait() if you don't want it as separate method
    }
}

Playground

这乍看起来有点奇怪,但它的好处是保留了方便的基于Drop的界面,而没有Option的开销,所有这些都是在安全的代码中实现的.

Rust相关问答推荐

空字符串转换为Box字符串时是否分配?<>

新创建的变量的绑定生存期

你是如何在铁 rust 一侧的金牛座获得应用程序版本的?

为什么这是&q;,而让&q;循环是无限循环?

如何向下转换到MyStruct并访问Arc Mutex MyStruct实现的方法?

JSON5中的变量类型(serde)

Rust编译器似乎被结果类型与anyhow混淆

你能在Rust中弃用一个属性吗?

为什么BufReader实际上没有缓冲短寻道?

为什么切片时需要参考?

在多核嵌入式 Rust 中,我可以使用静态 mut 进行单向数据共享吗?

有没有办法隐式绑定 let/match 操作的成员?

类型判断模式匹配panic

`移动||异步移动{...}`,如何知道哪个移动正在移动哪个?

n 个范围的笛卡尔积

在 Rust 中,将可变引用传递给函数的机制是什么?

将文件的第一行分别读取到文件的其余部分的最有效方法是什么?

Rust 中的通用 From 实现

tokio async rust 的 yield 是什么意思?

有没有办法隐藏类型定义?