【问题标题】:How to capture self consuming variable in a struct?如何在结构中捕获自耗变量?
【发布时间】:2017-02-14 08:02:49
【问题描述】:

我有一个 Reader 实例,其方法是 consumes the Reader and returns a Writer 和 Writer 可以类似地再次返回到 Reader。使用不可变是微不足道的,但我不知道如何向调用者隐藏不可变性,只是用可变的 Self 在幕后进行读写器的舞蹈。

基本上我想要这样的东西:

struct Container<'a> {
    reader: Reader<'a, File>
}

fn update(&mut self) {
    let writer = self.reader.as_writer();
    writer.write_something();
    self.reader = writer.as_reader();
}

这只会给出cannot move out of borrowed content 错误。试图在 Reader 周围添加 Box、Cell 或 RefCell,这会导致其他错误。

reader 可以隐藏在可变接口后面还是强制整个结构层次结构也可变? (即类似于 Haskell 中的 IO)


类型与实物匹配的自包含样本(我认为)

#[derive(Debug)]
struct NoCopy(u32);

#[derive(Debug)]
struct Flipper<'a, T: 'a> {
    data: &'a mut T,
}
#[derive(Debug)]
struct Flopper<'a, T: 'a> {
    data: &'a mut T,
}
impl<'a, T> Flipper<'a, T> {
    fn flip(self) -> Flopper<'a, T> {
        Flopper{data: self.data}
    }
}
impl<'a, T> Flopper<'a, T> {
    fn flop(self) -> Flipper<'a, T> {
        Flipper{data: self.data}
    }
}

#[derive(Debug)]
struct Container<'a, T: 'a> {
    flipper: Flipper<'a, T>,
}
impl<'a, T> Container<'a, T> {
    fn run(&mut self) {
        self.flipper = self.flipper.flip().flop();
    }
}

fn main() {
    let f = Flipper{data: &mut NoCopy(42)};
    let f = f.flip().flop();
    println!("f={:?}", f);

    let mut c = Container{flipper: f};
    c.run();
    println!("c={:?}", c);
}
错误[E0507]:无法移出借来的内容 --> src/main.rs:29:24 | 29 | self.flipper = self.flipper.flip().flop(); | ^^^^不能移出借来的内容

【问题讨论】:

    标签: struct rust


    【解决方案1】:

    更简单的解决方案是使用Option 来包装Reader

    Option 有一个take 方法返回内容(并将None 放入Option),然后您可以通过分配将Reader 放回去。

    struct Container<'a> {
        reader: Option<Reader<'a, File>>
    }
    
    fn update(&mut self) {
        let writer = self.reader.take().unwrap().as_writer();
                                 ^~~~~~~
        writer.write_something();
        self.reader = Some(writer.as_reader());
                      ^~~~~                  ^
    }
    

    【讨论】:

    • 我认为你在拍摄后错过了.unwrap(),但它可以工作。 /我拍了拍额头
    • 如果函数试图退出而不将Some 恢复给读者,有没有办法让它成为编译错误?
    • @TommiKomulainen:我不知道,不。曾经有人建议让 Rust 能够在 struct/enum 中“打孔”然后恢复它们……但在存在 panic! 和展开时很难确保。
    【解决方案2】:

    Matthieu's answer 的基础上,我认为在Option 周围添加一个(简单的)包装器来强制执行编译时检查是很简单的,该检查只允许“借用”Reader 实例,而之后需要另一个实例返回。

    struct State<T>(Option<T>);
    
    impl<T> State<T> {
        pub fn new(val: T) -> Self {
            State(Some(val))
        }
        pub fn modify<F>(&mut self, fun: F)
            where F: FnOnce(T) -> T
        {
            self.0 = Some(fun(self.0.take().unwrap()));
        }
    }
    

    用法:

    fn modify(&mut self) {
        self.state.modify(|reader| {
            let writer = reader.as_writer();
            writer.write_something();
            writer.as_reader()
        }
    }
    

    应该足够好以避免意外误用。虽然不知道panic! 的展开。

    可能没有遵循 Rust 命名约定,statemodify 来自 Haskell 的 State

    【讨论】:

    • 好吧,如果它在从关闭返回之前出现恐慌,其中会有一个None
    猜你喜欢
    • 2021-11-29
    • 2022-11-23
    • 1970-01-01
    • 2019-09-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-17
    • 1970-01-01
    • 2014-07-11
    相关资源
    最近更新 更多