简体   繁体   English

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

[英]Storing a closure in a structure — cannot infer an appropriate lifetime

I'm trying to implement the State monad in Rust ( State is effectively a wrapper over a function which takes original state and returns modified state and some result). 我正在尝试在Rust中实现State monad( State实际上是一个函数的包装器,它接受原始状态并返回修改后的状态和一些结果)。 This is how one can implement State in Haskell (monad operations where renamed to unit and bind for sake of simplicity): 这就是人们如何在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'

So I try to rewrite it in Rust: 所以我尝试在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)
    }
}

(Actually I'm not sure if the definition of the run field is legal — it's said to be a bug). (实际上我不确定run字段的定义是否合法 - 它被认为是一个错误)。

This code doesn't compile: 此代码无法编译:

/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

Seems like I need to explicitly specify the lifetime for the closure expression when instantiating State in unit , but I just don't know how, so I need help here. 好像我需要在unit实例化State时显式指定闭包表达式的生命周期,但我只是不知道如何,所以我需要帮助。 Thanks. 谢谢。


EDIT : Unfortunately, I cannot use proc s (as Vladimir suggested), because a State can be executed arbitrary number of times. 编辑 :不幸的是,我不能使用proc (如弗拉基米尔所建议的那样),因为一个State可以被执行任意次数。

The error is completely legitimate. 该错误完全合法。 Currently it is completely impossible to return closures which can be called multiple times from functions (unless they were passed to these functions as arguments). 目前,完全不可能返回可以从函数中多次调用的闭包(除非它们作为参数传递给这些函数)。

You can easily find out that you have made a mistake in lifetimes usage when you look for lifetime annotations position. 当您查找终身注释位置时,您可以很容易地发现您在生命周期中使用了一个错误。 When lifetime parameter is used only in arguments or only in return values, then there is a mistake in your code. 当lifetime参数仅用于参数或仅用于返回值时,则代码中存在错误。 This is exactly your case - unit has 'r lifetime parameter, but it is used only in return value. 这正是你的情况 - unit'r lifetime参数,但它仅用于返回值。

Your code fails to compile because you're creating stack closure and are trying to store it inside an object which will be returned to the caller. 您的代码无法编译,因为您正在创建堆栈闭包并尝试将其存储在将返回给调用者的对象中。 But the closure is created on the stack, and when you return from the function, that stack space is invalidated, ie your closure is destroyed. 但是闭包是在堆栈上创建的,当你从函数返回时,该堆栈空间无效,即你的闭包被破坏了。 Borrow checker prevents this. 借用检查器阻止了这一点。

Currently there are only two kinds of closures in Rust - stack boxed closures and one-off heap boxed closures (procs). 目前在Rust中只有两种闭包 - 堆栈盒装闭包和一次性堆盒装闭包(过程)。 You could do what you want with a proc, but you will be able to call it only once. 你可以用proc做你想做的事,但你只能调用一次。 I'm not sure if this is OK for your needs, but here you go: 我不确定这是否适合您的需求,但是你去了:

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

I had to add Send kind because procs need their environment to be sendable. 我不得不添加Send kind,因为procs需要他们的环境可以发送。

In future, when (or if) dynamically sized types land, you will be able to create regular boxed closure which can be called multiple times and which own their environment. 将来,当(或者如果)动态大小的类型着陆时,您将能够创建常规的盒装闭包,可以多次调用并拥有其环境。 But DST are still in design and development. 但DST仍处于设计和开发阶段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM