closures - 在结构中存储闭包——无法推断出合适的生命周期

标签 closures rust

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

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'

所以我尝试用 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 字段的定义是否合法 — said 是一个错误)。

此代码无法编译:

/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

似乎我需要在 unit 中实例化 State 时明确指定闭包表达式的生命周期,但我只是不知道如何,所以我在这里需要帮助.谢谢。


编辑:不幸的是,我不能使用proc(如Vladimir 建议的那样),因为State 可以执行任意次数。

最佳答案

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

在寻找lifetime annotations位置时,很容易发现自己在lifetimes usage上犯了错误。当生命周期参数仅在参数中使用或仅在返回值中使用时,您的代码中就会出现错误。这正是您的情况 - unit 具有 'r 生命周期参数,但它仅用于返回值。

您的代码无法编译,因为您正在创建 stack 闭包并试图将其存储在将返回给调用者的对象中。但是闭包是在堆栈上创建的,当您从函数返回时,该堆栈空间将失效,即您的闭包将被销毁。借用检查器可以防止这种情况。

目前 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)
    }
}

我必须添加 Send 类型,因为 proc 需要它们的环境是可发送的。

将来,当(或如果)动态大小的类型落地时,您将能够创建可以多次调用并拥有自己的环境的常规盒装闭包。但 DST 仍在设计和开发中。

关于closures - 在结构中存储闭包——无法推断出合适的生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21510694/

相关文章:

ios - iOS swift 中所有对象的相同引用

php - PHP 5.3 和 5.4 之间的闭包有什么区别?

rust - 如何将 Rust 闭包转换为 C 风格的回调?

javascript - 如何根据作用域/闭包更新解构的 JavaScript 变量?

javascript - MooTools/JavaScript 变量范围

rust - 为什么这个 HashMap 搜索比预期慢?

rust - 将具有生命周期的闭包存储在结构中

berkeley-db - 将 Rust 与 Berkeley DB 连接起来

rust - 是否可以使用变量作为格式的fill参数!宏?

scala - "Closures"是如何如此强大的抽象,以至于使用它来实现对象系统和基本控制结构?