#drop #consume #no-alloc

no-std consumeondrop

一个零成本的抽象,允许通过值来消耗Drop::drop中的self

2个版本

0.1.1 2023年5月24日
0.1.0 2023年5月24日

#1338Rust模式

Download history 522/week @ 2024-03-14 568/week @ 2024-03-21 736/week @ 2024-03-28 973/week @ 2024-04-04 1136/week @ 2024-04-11 1026/week @ 2024-04-18 847/week @ 2024-04-25 939/week @ 2024-05-02 932/week @ 2024-05-09 1211/week @ 2024-05-16 879/week @ 2024-05-23 932/week @ 2024-05-30 943/week @ 2024-06-06 979/week @ 2024-06-13 978/week @ 2024-06-20 884/week @ 2024-06-27

3,953 每月下载量
用于 drop_arena

MIT 许可证

17KB
276 代码行

consume_on_drop

一个零成本的抽象,允许通过值来消耗Drop::drop中的self

你想要在impl Drop时以值的形式调用函数吗?你需要同时解构和丢弃你的结构体吗?你想要一个方便的类型来临时为你的值提供一个新的Drop实现吗?这个crate就是为你准备的。

安全、零成本API

ConsumeOnDrop<T>是围绕T的一个透明包装器,所有提供的操作都是零成本的。

WithConsumer<T, Q>是一个有序对(T, Q)的包装器,其所有提供的操作都是零成本的。

这些API中的所有公共函数、方法和特性都是完全安全的。

使用最少的unsafe代码实现

ConsumeOnDrop的实现恰好有2行unsafe代码,都很容易通过Miri检查和测试。

WithConsumer的实现完全安全(除了它依赖于ConsumeOnDrop的公共API之外)。

在丢弃时以值的形式消耗你的类型

use consume_on_drop::{Consume, ConsumeOnDrop, WithConsumer};

struct T;

fn consume_t(_t: T) {
    println!("We consumed T")
}

impl Consume for T {
    fn consume(self) {
        consume_t(self)
    }
}

fn main () {
    let t = ConsumeOnDrop::new(T);  // A thin wrapper around T which calls T::consume on drop
    drop(t);
    let t = WithConsumer::new(T, consume_t); // Alternately, we can explicitly equip a T with a consumer.
    drop(t);
}

编写一个可以解构和丢弃的结构体

以下代码无法编译。

struct MutRef<'a> {
    inner: &'a mut i32,
}

impl<'a> MuRef<'a> {
    pub fn new(val: &'a mut i32) -> Self {
        Self { inner: val }
    }
    
    pub fn into_inner(self) -> &'a mut i32 {
        self.inner
    }
}

impl<'a> Drop for MutRef<'a> {
    fn drop(&mut self) {
        *self.inner += 1;
    }
}

但我们可以使用ConsumeOnDrop来使其工作

use consume_on_drop::{Consume, ConsumeOnDrop};

struct RawMut<'a> {
    inner: &'a mut i32,
}

impl<'a> Consume for RawMut<'a> {
    fn consume(self) {
        *self.inner += 1;
    }
}

struct MutRef<'a> {
    inner: ConsumeOnDrop<RawMut<'a>>,
}

impl<'a> MutRef<'a> {
    pub fn new(val: &'a mut i32) -> Self {
        Self { inner: ConsumeOnDrop::new(RawMut { inner: val })}
    }
    
    pub fn into_inner(self) -> &'a mut i32 {
        ConsumeOnDrop::into_inner(self.inner).inner
    }
}

请注意,这是一个零成本的抽象。我们可以使用Option<RawMut>来实现相同的效果,但这会带来运行时开销,并阻止我们使用空指针优化Option<MutRef>

暂时给你的类型一个不同的drop实现

有时,你需要暂时给你的类型一个新的drop实现,以防你提前返回或panic。如果在错误的时间发生panic,数据可能会处于无效状态。为了标记这一点,你可能希望“毒化”你的数据。

use consume_on_drop::WithConsumer;

struct Data {
    string: Option<String>,
}

impl Data {
    fn new(str: String) -> Self {
        Self { string: Some(str) }
    }
    
    fn extend(&mut self, str: String) {
        self.string.as_mut().unwrap().extend(str.chars())
    }
    
    fn poison(&mut self) {
        self.string = None;
    }
}

fn produce_string() -> String {
    panic!("Oh no, we panicked!");
}

fn extend_produce(data: &mut Data) {
    let mut data = WithConsumer::new(data, Data::poison);
    data.extend(produce_string()); // if produce_string panics, we will drop data here and poison it
    WithConsumer::into_inner(data); // but if there's no panic, we will not poison.
}

许可证:MIT许可证

无运行时依赖