rust 如果需要一个函数类型作为类型参数,如何键入None?

xxhby3vn  于 2023-01-17  发布在  其他
关注(0)|答案(3)|浏览(145)

假设我有一个接受回调的函数

fn foo<T>(callback: impl FnOnce(T) -> T, value: T) -> T {
    callback(value)
}

假设我现在想让这个回调函数成为可选函数,在我看来,最明显的方法就是使用Option

fn foo<T>(callback: Option<impl FnOnce(T) -> T>, value: T) -> T {
    if let Some(callback) = callback {
        callback(value)
    } else {
        value
    }
}

然而,当这样做时,我在使用站点遇到了一个问题。例如,下面的代码不能编译:

fn bar() -> u8 {
    let value: u8 = b'.';
    let value = foo(None, value);
    value
}

出现以下错误:

error[E0282]: type annotations needed
  --> XX:XX:XX
   |
XX |     let value = foo(None, value);
   |                     ^^^^ cannot infer type of the type parameter `T` declared on the enum `Option`
   |
help: consider specifying the generic argument
   |
XX |     let value = foo(None::<T>, value);
   |                         +++++

For more information about this error, try `rustc --explain E0282`.

但是,似乎不可能为此提供类型,因为impl …语法在这里不能用作类型参数,而且无论如何都需要闭包中的具体类型。
是否可以像这样注解缺少闭包的类型?

sd2nnvve

sd2nnvve1#

你必须给它提供一个实现FnOnce(u8) -> u8的类型,因为这是foo所需要的,我能想到的最简单的一个是函数指针:

fn bar() -> u8 {
    let value: u8 = b'.';
    let value = foo(None::<fn(_) -> _>, value);
    value
}
eoigrqb6

eoigrqb62#

有几种方法。你需要选择一个实现FnOnce(T) -> T的类型。对于示例,我将使用fn(T) -> T,但它没有太大区别:

  • 用河豚鱼:
foo(None::<fn(u8) -> u8>, 123);
  • 使用带有类型属性的字母绑定
let option_fn: Option<fn(u8) -> u8> = None;
foo(option_fn, 123);
  • 生成名为的泛型,并在foo上使用turbofish:
fn foo<T, F: FnOnce(T) -> T>(f: F, t: T) -> T {
  ...
}

foo::<u8, fn(u8) -> u8>(None, 123);
eufgjt7s

eufgjt7s3#

作为现有答案的替代方案,提供一个恒等函数(返回其参数的函数)将是更合适的函数风格,其优点是可以解决整个问题,而且它还可能表现得更好,因为没有任何分支可以被错误地预测。

fn identity<T>(v: T) -> T { v }

fn foo<T>(callback: impl FnOnce(T) -> T, value: T) -> T {
    callback(value)
}

fn bar() -> u8 {
    let value: u8 = b'.';
    let value = foo(identity, value);
    value
}

换句话说,使用Option实际上是“浪费”,因为“没有函数”会导致与“恒等函数”相同的可观察行为。
(Of当然,|v| videntity短,所以使用哪一个是一个品味问题。)
这并没有解决当恒等函数不应该导致和没有函数一样的行为时,如何在可选函数的上下文中指定None的一般问题。其他答案解决了如何处理这种情况。这个答案旨在当两种情况 * 确实 * 具有相同的行为时提供更好的解决方案。

相关问题