gpt4 book ai didi

rust - 如何在用作函数结果的闭包内应用特征

转载 作者:行者123 更新时间:2023-11-29 08:10:08 25 4
gpt4 key购买 nike

我无法实现这个 LISP 构造

(defun foo (n)
(lambda (i) (incf n i)))

使用rust 。我试过this :

use std::ops::Add;

fn f<T: Add>(n: T) -> Box<Fn(T) -> T> {
Box::new(move |i: T| n + i)
}

fn main() {
let adder = f(2);
assert_eq!(4, adder(2));
}

但它会导致错误:

error: mismatched types:
expected `T`,
found `<T as core::ops::Add>::Output`
(expected type parameter,
found associated type) [E0308]
Box::new(move |i: T| n + i)
^~~~~

似乎为外部函数定义的特征 Add 没有转移到内部闭包中。

是否可以实现这样的构造?

可以用具体类型而不是泛型来实现这个函数:

fn f(n: i32) -> Box<Fn(i32) -> i32> {
Box::new(move |i| n + i)
}

最佳答案

通用版本有几个问题。

首先,您提供的错误发生是因为 T: Add不足以指定输出类型:您需要对关联类型施加约束 <T as Add>::Output以及(参见 Add 文档):

fn f<T: Add<Output=T>>(n: T) -> Box<Fn(T) -> T> {
Box::new(move |i: T| n + i)
}

或者,您可以让闭包返回 <T as Add> 的输出类型:

fn f<T: Add>(n: T) -> Box<Fn(T) -> T::Output> {
Box::new(move |i: T| n + i)
}

但是,现在您会收到以下错误:

<anon>:4:10: 4:37 error: the parameter type `T` may not live long enough [E0310]
<anon>:4 Box::new(move |i: T| n + i)
^~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:4:10: 4:37 help: see the detailed explanation for E0310
<anon>:4:10: 4:37 help: consider adding an explicit lifetime bound `T: 'static`...
<anon>:4:10: 4:37 note: ...so that the type `[closure@<anon>:4:19: 4:36 n:T]` will meet its required lifetime bounds
<anon>:4 Box::new(move |i: T| n + i)
^~~~~~~~~~~~~~~~~~~~~~~~~~~

这里的问题是 T可能在其中包含引用(之后,它是一个通用类型 - 它可以包含任何内容);然而,Box<Fn(T) -> T>隐式意味着此特征对象内的任何内容都必须是 'static ,即编译器自动添加 'static约束:Box<Fn(T) -> T + 'static> .但是,您的闭包会捕获 T其中可以包含任何引用,而不仅仅是 'static .

修复它的最通用方法是添加显式生命周期约束 TBox<Fn(T) -> T> :

fn f<'a, T: Add<Output=T> + 'a>(n: T) -> Box<Fn(T) -> T + 'a> {
Box::new(move |i: T| n + i)
}

或者,您可以指定 T'static ,尽管这不必要地限制了代码的通用性:

fn f<T: Add<Output=T> + 'static>(n: T) -> Box<Fn(T) -> T> {
Box::new(move |i: T| n + i)
}

然而,这仍然无法编译:

<anon>:4:31: 4:32 error: cannot move out of captured outer variable in an `Fn` closure
<anon>:4 Box::new(move |i: T| n + i)
^

发生此错误是因为 Rust 中的加法(即 Add 特征)按值工作 - 它消耗两个参数。对于 Copy类型,比如数字,没关系——它们总是被复制。但是,编译器不能假定泛型类型参数也指定 Copy类型,因为没有相应的界限,因此它假定类型为 T 的值只能四处移动。但是,您指定返回的闭包是 Fn ,因此它通过引用获取其环境。您不能移出引用,这就是此错误的原因。

有多种方法可以修复此错误,最简单的方法是添加 Copy绑定(bind):

fn f<'a, T: Add<Output=T> + Copy + 'a>(n: T) -> Box<Fn(T) -> T + 'a> {
Box::new(move |i: T| n + i)
}

现在它可以编译了。

一种可能的替代方法是返回 FnOnce 按值获取环境的闭包:

fn f<'a, T: Add<Output=T> + 'a>(n: T) -> Box<FnOnce(T) -> T + 'a> {
Box::new(move |i: T| n + i)
}

但是,它有两个问题。首先,顾名思义,FnOnce只能调用一次,因为在第一次调用时它的环境被消耗掉了,下次就没有什么可以调用的了。这可能过于局限。其次,不幸的是,Rust 不能调用 Box<FnOnce()>完全关闭。这是一个实现问题,应该在未来解决;现在有一个不稳定的 FnBox trait 来解决这个问题。

甚至另一种选择是使用引用而不是值:

fn f<'a, T: 'a>(n: T) -> Box<Fn(T) -> T + 'a> where for<'b> &'b T: Add<T, Output=T> {
Box::new(move |i: T| &n + i)
}

现在我们指定它而不是 T , &'b T终生'b必须与 T 相加.这里我们使用 Add 的事实trait 也被重载以引用原始类型。这可能是该函数最通用的版本。

关于rust - 如何在用作函数结果的闭包内应用特征,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34063272/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com