摘要:我正在使用资源使用者类型Consumer和资源类型Resource.接口的相关部分如下:

impl Consumer {
   pub fn consume(&self, r: Resource);
}

API是这样的,所以没有其他安全的方式让Consumer人消费资源.Resource不是Copy.

我想创建一个具有以下接口的类型WithConsumer<‘a>:

impl<‘a> WithConsumer<‘a> {
    pub fn new(r: Resource, c: &’a Consumer) -> Self;
    pub fn into_inner(self) -> Resource;
}

impl<‘a> AsRef<‘a, Resouce> for WithConsumer<‘a>;

impl<‘a> AsMut<‘a, Resource> for WithConsumer<‘a>;

impl<‘a> Drop for WithConsumer<‘a>;

其中,代码std::men::drop(WithConsumer::new(resource, consumer))应等于consumer.consume(resource),并且所有其他实现细节都应具有其明显的属性.

显然,这不能在安全Rust 中做到.以下是一个不安全的实现(明显省略了几个位):

use std::mem::ManuallyDrop;

pub struct WithConsumer<‘a> {
   r: ManuallyDrop<Resource>,
   c: &’a Consumer,
}

impl WithConsumer<‘a> {
    pub fn into_inner(mut self) -> Resource {
        unsafe {
            let answer = ManuallyDrop::take(&mut self.r);
            std::mem::forget(self);
            answer
        }
    }
}

impl<‘a> Drop for WithConsumer<‘a> {
    fn drop(&mut self) {
        unsafe {       self.c.consume(ManuallyDrop::take(&mut self.r))
        }
    }
}

这是一个安全的抽象概念吗?在crates.io上,标准库或任何好的 crate 中有没有已经做过这样的事情的部分?这似乎是一个足够明显的问题,它应该有一个众所周知的、已经写好的解决方案.

推荐答案

您可以并且应该使用Option而不是不安全的代码.这引入了unwrap(),但除非实现中有错误,否则它们都不会panic ,因为当WithConsumer即将被丢弃时,Option只是None.

pub struct Resource;

pub struct Consumer;

impl Consumer {
    pub fn consume(&self, _r: Resource) {}
}

pub struct WithConsumer<'a> {
    r: Option<Resource>,
    c: &'a Consumer,
}

impl<'a> WithConsumer<'a> {
    pub fn new(r: Resource, c: &'a Consumer) -> Self {
        Self { r: Some(r), c }
    }
    pub fn into_inner(mut self) -> Resource {
        self.r.take().unwrap()
    }
}

impl AsRef<Resource> for WithConsumer<'_> {
    fn as_ref(&self) -> &Resource {
        self.r.as_ref().unwrap()
    }
}

impl AsMut<Resource> for WithConsumer<'_> {
    fn as_mut(&mut self) -> &mut Resource {
        self.r.as_mut().unwrap()
    }
}

impl Drop for WithConsumer<'_> {
    fn drop(&mut self) {
        if let Some(r) = self.r.take() {
            self.c.consume(r);
        }
    }
}

这确实使用了WithConsumer中的一个额外字节来存储Option状态(除非Resource类型有一个可以使用的niche).但是,除非您对内存使用效率有非常强烈的要求,否则使用不安全代码来避免这种情况不是明智的权衡.

Rust相关问答推荐

PyReadonlyArray2到Vec T<>

trait声明中的生命周期参数

函数内模块的父作用域的访问类型

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

将一个泛型类型转换为另一个泛型类型

如何创建一个可变的嵌套迭代器?

允许 rust 迹 struct 条目具有多种类型

类型生命周期绑定的目的是什么?

Rust并发读写引起的死锁问题

Rust: 目标成员属于哪个"目标家族"的列表是否存在?

Rust编译器通过哪些规则来确保锁被释放?

Rust 程序中的内存泄漏

按下 Ctrl + C 时优雅地停止命令并退出进程

在 Bevy 项目中为 TextureAtlas 精灵实施 NearestNeighbor 的正确方法是什么?

产生拥有值的迭代器的 Rust 可变borrow 在循环中失败

为什么传递 option.as_ref 的行为不同于使用匹配块并将内部映射到 ref 自己?

在 RefCell 上borrow

如何将切片推入数组?

将数据序列化为 struct 模型,其中两个字段的数据是根据 struct 中的其他字段计算的

覆盖类型的要求到底是什么?为什么单个元素元组满足它?