我正在尝试在Rust写一个小程序,但我无法让它工作。

我在一个较小的脚本中重现了错误:

fn main() {
    let name = String::from("World");
    let test = simple(name);
    println!("Hello {}!", test())
}

fn simple<T>(a: T) -> Box<Fn() -> T> {
    Box::new(move || -> T {
        a
    })
}

当我编译它时,我收到此错误:

error[E0310]: the parameter type `T` may not live long enough
  --> test.rs:8:9
   |
7  |       fn simple<T>(a: T) -> Box<Fn() -> T> {
   |                 - help: consider adding an explicit lifetime bound `T: 'static`...
8  | /         Box::new(move || -> T {
9  | |             a
10 | |         })
   | |__________^
   |
note: ...so that the type `[closure@test.rs:8:18: 10:10 a:T]` will meet its required lifetime bounds
  --> test.rs:8:9
   |
8  | /         Box::new(move || -> T {
9  | |             a
10 | |         })
   | |__________^

我试图添加一个明确的生命周期绑定T: 'static如错误所示,但我得到一个新的错误:

error[E0507]: cannot move out of captured outer variable in an `Fn` closure
 --> test.rs:9:13
  |
7 |     fn simple<T: 'static>(a: T) -> Box<Fn() -> T> {
  |                           - captured outer variable
8 |         Box::new(move || -> T {
9 |             a
  |             ^ cannot move out of captured outer variable in an `Fn` closure

#1楼 票数:7 已采纳

这里有几件事情,这一切都与移动语义和闭包有轻微的尴尬有关。

首先, simple函数需要为其T参数指定生命周期。 从函数的角度来看, T可以是任何类型,这意味着它可以是一个引用,因此它需要有一个生命周期。 终身省略不适用于此案例,因此您需要明确写出来。 编译器建议'static ,这对于hello world来说很好。 如果你有更复杂的生命周期,你需要使用一个生命周期参数; 请参阅下面的示例了解更多信息

你的关闭不能是Fn ,因为你不能多次调用它。 正如你所说的新错误所说,你的闭包在它被调用时将它捕获的值( a )移出闭包。 这是同样的事情的话称它是需要方法self ,而不是&self 如果函数调用是普通方法而不是特殊语法,那么它将是这样的:

trait FnOnce {
    type Output
    fn call(self) -> Output
}

trait Fn : FnOnce {
    fn call(&self) -> Output
}

// generated type
struct MyClosure<T> {
    a: T
}

impl<T> FnOnce for MyClosure<T> {
    fn call(self) -> T { self.a }
}

(这并不比这些类型的实际定义简单得多。)

简而言之,一个消耗其捕获值的闭包不实现Fn ,只实现FnOnce 调用它会消耗关闭。 还有一个FnMut但这里没有关系。

这有另一个含义,就是当它们被移动时消耗它们。 您可能已经注意到,你不能调用了使用方法self任何特质的对象(在Box<T>其中T是性状)。 要移动对象,移动它的代码需要知道要移动的对象的大小。 对于未标注的特征对象,不会发生这种情况。 这也适用于Box<FnOnce> 由于调用闭包移动它(因为调用是一个self `),你不能调用闭包。

那么如何解决这个问题呢? 它使Box<FnOnce>有点无用。 有两种选择。

如果你可以使用不稳定的Rust,你可以使用FnBox类型:它取代了在Box内部工作的FnOnce 这是一个特点门后隐藏,因为,如文档向您发出警告:“请注意, FnBox可能在未来被废弃,如果Box<FnOnce()>关闭成为直接使用。” 这是一个使用此解决方案的操场,并添加了生命周期参数来修复原始问题。

可能是更广泛适用的工程解决方案的替代方案是避免移出封盖。

  • 如果你总是将静态对象放入闭包中,你可以返回一个引用&'static T 这样你可以根据需要多次调用闭包,并且所有调用者都可以获得对同一对象的引用。

  • 如果对象不是静态的,则可以返回Rc<T> 在这种情况下,所有调用者仍然获得对同一对象的引用,并且该对象的生命周期是动态管理的,因此只要需要它就会保持活动状态。 这是实现此选项的另一个游乐场。

  • 您可以让闭包将其参数复制到每个调用者。 这样可以根据需要多次调用它,每个调用者都可以获得自己的副本。 不需要进一步的终身管理。 如果以这种方式实现它,您仍然可以使参数为Rc<T>而不是T以使用与上述选项相同的方式使用该函数。

#2楼 票数:0

simple函数返回一个在返回的类型T是通用的闭包。

这意味着返回的类型可以是任何内容,例如引用或包含引用的类型,因此编译器建议在类型上指定'static

fn simple<T: 'static>(a: T) -> Box<Fn() -> T> {
    Box::new(move || -> T { a })
}

但现在你遇到了问题:

error[E0507]: cannot move out of captured outer variable in an `Fn` closure
 --> src/main.rs:2:29
  |
1 | fn simple<T: 'static>(a: T) -> Box<Fn() -> T> {
  |                       - captured outer variable
2 |     Box::new(move || -> T { a })
  |                             ^ cannot move out of captured outer variable in an `Fn` closure

error[E0597]: `name` does not live long enough
 --> src/main.rs:7:24
  |
7 |     let test = simple(&name);
  |                        ^^^^ borrowed value does not live long enough
8 |     println!("Hello {}!", test())
9 | }
  | - borrowed value only lives until here
  |
  = note: borrowed value must be valid for the static lifetime...

由于捕获的变量name由外部“主”上下文拥有,因此不能被其他人“窃取”。

接下来要尝试的是通过引用传递参数,注意定义盒装Fn特征的生命周期。

实现Fn trait的盒装闭包存在于堆上,并且必须明确赋值: Fn() -> &'a T` + 'a

fn main() {
    let name = String::from("World");
    let test = simple(&name);
    println!("Hello {}!", test())
}

fn simple<'a, T: 'a>(val: &'a T) -> Box<Fn() -> &'a T + 'a> {
    Box::new(move || -> &'a T { val })
}

另一种解决方案是使用impl trait,从Rust 1.26开始提供:

fn main() {
    let name = String::from("World");
    let test = simple(&name);
    println!("Hello {}!", test())
}

fn simple<'a, T: 'a>(val: &'a T) -> impl Fn() -> &'a T {
    move || -> &'a T { val }
}

  ask by b1zzu translate from so

未解决问题?本站智能推荐:

1回复

传递给函数中的泛型闭包时,泛型结构的寿命不够长

我正在尝试编写一个简单的实用程序函数(用于测试),它包装给定的闭包并公开一些与数据库相关的对象。 它看起来像这样: pub fn with_repository<'a, R, F>(f: F) where R: Repository<'a>, F: FnOnce(R,
1回复

Rust 说函数参数不够活跃,即使已经设置了适当的生命周期

对于背景上下文:我正在创建一个基于观察者/订阅者的全局事件系统(使用单个共享事件系统)。 我决定使用FnMut作为我的回调闭包。 放置在临时结构Data<'a>的impl中的生命周期'a应该允许方法mut_func()的callback参数与整个Data结构一样长。 因为callback
1回复

包含闭包参数的方法需要错误的类型?

我有一个结构体Parser ,它包含一个transformer - 一个用于修改其ParserState的函数 - 和所说的ParserState 。 #[derive(Clone)] pub struct Parser<F> where F: Fn(ParserState)
1回复

是否可以在具有泛型参数和返回类型的结构中使用闭包?

是否可以用泛型参数和返回类型替换下面的闭包? 例如,我可以在该结构中创建Fn<U, V>(x: U) -> V并创建一个构造函数吗?
1回复

闭包:预期的 u32 发现类型参数

问题是我的泛型类型T只接受来自u32参数,在这种情况下,我试图传递一个我存储在U的泛型值。 有没有办法将泛型转换为特定类型? 我能做些什么来解决问题?
1回复

尽管有“impl Trait”参数,但仍提供显式类型规范

考虑以下最小示例: 无法编译,因为使用了impl Trait : 尽管在另一个参数中使用了impl Trait ,我是否有可能为T指定类型? 我在一个代码库中工作,我需要调用一个使用impl Trait的泛型函数,但我需要指定其他泛型类型。
1回复

如何在Rust中命名关联函数的类型?

我正在尝试编写通用的代码,但对于常用的代码也具有便利功能,例如加法,我想为所有T: Add类型定义它。 如果我定义独立函数,则可以正常工作,因为我可以在返回值中使用impl Trait来隐藏不透明类型。 但是,如果要在特征中定义此函数,则必须将<T as Add>::add的类型命名
1回复

为什么移动闭包不捕获具有通用类型的值?

我正在尝试创建一个ScopeRunner类型,该类型可以存储对实现Scope特征的类型的方法的方法调用,如下所示: trait Scope { fn run(&self) -> String; } struct ScopeImpl; impl Scope for Sc