当前位置:  开发笔记 > 后端 > 正文

如何在用作函数结果的闭包内应用trait

如何解决《如何在用作函数结果的闭包内应用trait》经验,为你挑选了1个好方法。

我无法实现这种LISP结构

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

在Rust.我试过这个:

use std::ops::Add;

fn f(n: T) -> Box 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 `::Output`
(expected type parameter,
    found associated type) [E0308]
           Box::new(move |i: T| n + i)
                                ^~~~~

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

是否有可能实施这样的建设?

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

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

Vladimir Mat.. 7

通用版本存在几个问题.

首先,您提供的错误是因为仅仅T: Add不足以指定输出类型:您还需要将约束放在关联类型上::Output(请参阅Add文档):

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

或者,您可以使闭包返回以下输出类型:

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

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

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

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

修复它的最常用方法是添加明确的生命周期约束TBox T>:

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

或者,你可以指定T就是'static,虽然这不必要的限制了你的代码的通用性:

fn f + 'static>(n: T) -> Box T> {
    Box::new(move |i: T| n + i)
}

但是,这仍然无法编译:

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

发生此错误是因为Rust(即Addtrait)中的添加按值工作 - 它消耗两个参数.对于Copy类型,如数字,它很好 - 它们总是被复制.但是,编译器不能假设泛型类型参数也指定Copy类型,因为没有相应的边界,因此它假定类型的值T只能移动.但是,您指定返回的闭包是Fn,因此它通过引用获取其环境.您无法移出引用,这就是此错误的含义.

有几种方法可以解决此错误,最简单的方法是添加Copy绑定:

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

现在它编译.

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

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

然而,它有两个问题.首先,正如其名称所暗示的那样,FnOnce只能调用一次,因为在第一次调用时,它的环境被消耗掉,并且下次没有任何东西可以调用它.这可能过于局限.其次,不幸的是,Rust根本无法调用Box闭包.这是一个实施问题,将来应该解决; 现在有一个不稳定的FnBox特点可以解决这个问题.

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

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

现在我们指定代替T,&'b T对于任何生命周期'b必须与之相加T.这里我们使用Addtrait重载的事实来引用原始类型.这可能是此功能的最通用版本.



1> Vladimir Mat..:

通用版本存在几个问题.

首先,您提供的错误是因为仅仅T: Add不足以指定输出类型:您还需要将约束放在关联类型上::Output(请参阅Add文档):

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

或者,您可以使闭包返回以下输出类型:

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

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

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

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

修复它的最常用方法是添加明确的生命周期约束TBox T>:

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

或者,你可以指定T就是'static,虽然这不必要的限制了你的代码的通用性:

fn f + 'static>(n: T) -> Box T> {
    Box::new(move |i: T| n + i)
}

但是,这仍然无法编译:

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

发生此错误是因为Rust(即Addtrait)中的添加按值工作 - 它消耗两个参数.对于Copy类型,如数字,它很好 - 它们总是被复制.但是,编译器不能假设泛型类型参数也指定Copy类型,因为没有相应的边界,因此它假定类型的值T只能移动.但是,您指定返回的闭包是Fn,因此它通过引用获取其环境.您无法移出引用,这就是此错误的含义.

有几种方法可以解决此错误,最简单的方法是添加Copy绑定:

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

现在它编译.

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

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

然而,它有两个问题.首先,正如其名称所暗示的那样,FnOnce只能调用一次,因为在第一次调用时,它的环境被消耗掉,并且下次没有任何东西可以调用它.这可能过于局限.其次,不幸的是,Rust根本无法调用Box闭包.这是一个实施问题,将来应该解决; 现在有一个不稳定的FnBox特点可以解决这个问题.

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

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

现在我们指定代替T,&'b T对于任何生命周期'b必须与之相加T.这里我们使用Addtrait重载的事实来引用原始类型.这可能是此功能的最通用版本.

推荐阅读
喜生-Da
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有