在结构中存储闭包 - 无法推断出适当的寿命

nam*_*ess 6 closures rust

我正在尝试在Rust中实现State monad(State实际上是一个函数的包装器,它接受原始状态并返回修改后的状态和一些结果).这是一个如何能实现State在Haskell(其中更名为单子的操作unit,并bind为简单起见):

data State s u = State { run :: s -> (u, s) }

-- return
unit :: u -> State s u
unit u = State $ \s -> (u, s)

-- (>>=)
bind :: State s u -> (u -> State s a) -> State s a
bind m f = State $ \s ->
             let (u, s') = run m s
             in run (f u) s'
Run Code Online (Sandbox Code Playgroud)

所以我尝试在Rust中重写它:

pub struct State<'r, S, U> {
    priv run: 'r |S| -> (U, S)
}

pub fn unit<'r, S, U>(value: U) -> State<'r, S, U> {
    State {
        run: |state| (value, state)
    }
}
Run Code Online (Sandbox Code Playgroud)

(实际上我不确定该run领域的定义是否合法 - 据说这是一个错误).

此代码无法编译:

/some/path/lib.rs:31:12: 31:36 error: cannot infer an appropriate lifetime due to conflicting requirements
/some/path/lib.rs:31         run: |state| (value, state)
                                ^~~~~~~~~~~~~~~~~~~~~~~~
/some/path/lib.rs:29:52: 33:2 note: first, the lifetime cannot outlive the block at 29:51...
/some/path/lib.rs:29 pub fn unit<'r, S, U>(value: U) -> State<'r, S, U> {
/some/path/lib.rs:30     State {
/some/path/lib.rs:31         run: |state| (value, state)
/some/path/lib.rs:32     }
/some/path/lib.rs:33 }
/some/path/lib.rs:31:12: 31:36 note: ...so that closure does not outlive its stack frame
/some/path/lib.rs:31         run: |state| (value, state)
                                ^~~~~~~~~~~~~~~~~~~~~~~~
/some/path/lib.rs:29:52: 33:2 note: but, the lifetime must be valid for the lifetime &'r  as defined on the block at 29:51...
/some/path/lib.rs:29 pub fn unit<'r, S, U>(value: U) -> State<'r, S, U> {
/some/path/lib.rs:30     State {
/some/path/lib.rs:31         run: |state| (value, state)
/some/path/lib.rs:32     }
/some/path/lib.rs:33 }
/some/path/lib.rs:30:5: 30:10 note: ...so that types are compatible (expected `State<'r,S,U>` but found `State<,S,U>`)
/some/path/lib.rs:30     State {
                         ^~~~~
error: aborting due to previous error
Run Code Online (Sandbox Code Playgroud)

好像我需要实例化时明确指定寿命为封表达Stateunit,但我就是不知道怎么回事,所以我需要在这里帮忙.谢谢.


编辑:不幸的是,我不能使用procs(作为弗拉基米尔建议),因为一个State可以执行任意次数.

Vla*_*eev 7

该错误完全合法.目前,完全不可能返回可以从函数中多次调用的闭包(除非它们作为参数传递给这些函数).

当您查找终身注释位置时,您可以很容易地发现您在生命周期中使用了一个错误.当lifetime参数仅用于参数或仅用于返回值时,则代码中存在错误.这正是你的情况 - unit'r生命周期参数,但它仅用于返回值.

您的代码无法编译,因为您正在创建堆栈闭包并尝试将其存储在将返回给调用者的对象中.但是闭包是在堆栈上创建的,当你从函数返回时,该堆栈空间无效,即你的闭包被破坏了.借用检查器阻止了这一点.

目前Rust中只有两种闭包 - 堆栈盒装闭包和一次性堆盒装闭包(procs).你可以用proc做你想做的事,但你只能调用一次.我不确定这是否适合您的需求,但是你去了:

pub struct State<S, U> {
    priv run: proc(S) -> (U, S)
}

pub fn unit<S: Send, U: Send>(value: U) -> State<S, U> {
    State {
        run: proc(state) (value, state)
    }
}
Run Code Online (Sandbox Code Playgroud)

我不得不添加Send种类,因为procs需要他们的环境可以发送.

将来,当(或者如果)动态大小的类型着陆时,您将能够创建常规的盒装闭包,可以多次调用并拥有其环境.但DST仍处于设计和开发阶段.

  • FWIW,Rust社区不会将`||'称为未装箱的闭包:`||`相当于一个`&Fn <...>`特征对象(对于一个假设的`Fn`特征),即它被装入一个特征对象,其中数据(在这种情况下为闭包环境)位于堆栈上.一个"unboxed closure"是`&x as&Fn <...>`中的`x`,它还无法创建,但正在进行工作.(我认为这项工作比DST更有意义.[讨论开始](https://mail.mozilla.org/pipermail/rust-dev/2013-December/007594.html).) (3认同)
  • 你是对的.此外,还有自己的封闭装置,也被拆除了.希望DST能带回来. (2认同)