如何编写一个接受函数片段的函数?
How to write a function that takes a slice of functions?
我正在尝试编写一个采用函数片段的函数。考虑以下简单的 illustration:
fn g<P: Fn(&str) -> usize>(ps: &[P]) { }
fn f1() -> impl Fn(&str) -> usize { |s: &str| s.len() }
fn f2() -> impl Fn(&str) -> usize { |s: &str| s.len() }
fn main() {
g(&[f1(), f2()][..]);
}
编译失败:
error[E0308]: mismatched types
--> src/main.rs:6:15
|
6 | g(&[f1(), f2()][..]);
| ^^^^ expected opaque type, found a different opaque type
|
= note: expected type `impl for<'r> std::ops::Fn<(&'r str,)>` (opaque type)
found type `impl for<'r> std::ops::Fn<(&'r str,)>` (opaque type)
有什么办法吗?
你的问题是数组的每个元素都必须是同一类型,但是声明为 returning impl Trait
的函数的 return 是一个 不透明类型,这是一个未指定的、未命名的类型,您只能通过给定的特征使用它。
您有两个 return 相同 impl Trait
的函数,但这并不意味着它们 return 相同的类型。事实上,正如您的编译器所显示的那样,它们是不同的不透明类型,因此它们不能属于同一数组。如果你要写一个相同类型值的数组,比如:
g(&[f1(), f1(), f1()]);
那就可以了。但是不同的功能,就会有不同的类型,数组是无法搭建的。
这是否意味着您的问题没有解决方案?当然不是!您只需要调用动态调度。那就是你必须制作 &[&dyn Fn(&str) -> usize]
类型的切片。为此,您需要做两件事:
- 添加一个间接级别:动态调度总是通过引用或指针完成(
&dyn Trait
或 Box<dyn Trait>
而不是 Trait
)。
- 对
&dyn Trait
进行显式转换以避免转换中出现歧义。
有多种方法可以进行强制转换:可以强制转换数组的第一个元素,也可以声明临时变量,或者为切片指定类型。我更喜欢后者,因为它更对称。像这样:
fn main() {
let fns: &[&dyn Fn(&str) -> usize] =
&[&f1(), &f2()];
g(fns);
}
使用此解决方案 Link 到 playground。
我正在尝试编写一个采用函数片段的函数。考虑以下简单的 illustration:
fn g<P: Fn(&str) -> usize>(ps: &[P]) { }
fn f1() -> impl Fn(&str) -> usize { |s: &str| s.len() }
fn f2() -> impl Fn(&str) -> usize { |s: &str| s.len() }
fn main() {
g(&[f1(), f2()][..]);
}
编译失败:
error[E0308]: mismatched types
--> src/main.rs:6:15
|
6 | g(&[f1(), f2()][..]);
| ^^^^ expected opaque type, found a different opaque type
|
= note: expected type `impl for<'r> std::ops::Fn<(&'r str,)>` (opaque type)
found type `impl for<'r> std::ops::Fn<(&'r str,)>` (opaque type)
有什么办法吗?
你的问题是数组的每个元素都必须是同一类型,但是声明为 returning impl Trait
的函数的 return 是一个 不透明类型,这是一个未指定的、未命名的类型,您只能通过给定的特征使用它。
您有两个 return 相同 impl Trait
的函数,但这并不意味着它们 return 相同的类型。事实上,正如您的编译器所显示的那样,它们是不同的不透明类型,因此它们不能属于同一数组。如果你要写一个相同类型值的数组,比如:
g(&[f1(), f1(), f1()]);
那就可以了。但是不同的功能,就会有不同的类型,数组是无法搭建的。
这是否意味着您的问题没有解决方案?当然不是!您只需要调用动态调度。那就是你必须制作 &[&dyn Fn(&str) -> usize]
类型的切片。为此,您需要做两件事:
- 添加一个间接级别:动态调度总是通过引用或指针完成(
&dyn Trait
或Box<dyn Trait>
而不是Trait
)。 - 对
&dyn Trait
进行显式转换以避免转换中出现歧义。
有多种方法可以进行强制转换:可以强制转换数组的第一个元素,也可以声明临时变量,或者为切片指定类型。我更喜欢后者,因为它更对称。像这样:
fn main() {
let fns: &[&dyn Fn(&str) -> usize] =
&[&f1(), &f2()];
g(fns);
}
使用此解决方案 Link 到 playground。