fn 的可选通用类型参数?

Optional generic type Parameter for fn?

我想要一个函数有一个可选的通用类型参数来做这样的事情:

fn main() {
    bar::<()>();
}

fn bar<F: Foo>() {
    let x = some_computation();
    if F != () {
        let foo = F::new(x);
        foo.foo();
    }
}

trait Foo {
    fn new(x: u64) -> Self;
    fn foo(&self);
}

有没有办法拥有一个可选的类型参数?如果是这样,有没有办法检查类型参数是否存在于函数中?

我想答案是否定的,但是否可以使用宏来做到这一点?

您无法匹配类型本身,但从外观上看,您希望根据类型获得不同的功能。最好的方法是 impl Foo 用于您希望能够处理的任何类型。这是我对您的代码示例的解释:

fn main() {
    let a = Bar::from(()).some_completion();
    let b = Bar::from(1234).some_completion();
}

trait Foo {
    fn some_completion(&self) -> Self;
}

struct Bar {}

impl From<u64> for Bar {
    fn from(x: u64) -> Self {
        todo!();
    }
}

impl From<()> for Bar {
    fn from(x: ()) -> Self {
        todo!();
    }
}

impl Foo for Bar {
    fn some_completion(&self) -> Self {
        Self { /* something different */ }
    }
}

如果您确实需要泛型函数,std::str::parse 是一个很好的示例,说明如何传入类型(在本例中为数字类型)并更改 return 类型。

一般情况下,您不能为函数指定默认类型参数,并且除了通过 trait 上的方法外,您无法区分类型参数。

最简单的解决方法是只有两个函数,例如 barbar_with,其中一个不接受类型参数,另一个接受一个类型参数:

// call with no type parameters
pub fn bar() {
    let x = some_computation();
}

// call with type parameter
pub fn bar_with<F: Foo>() {
    let x = some_computation();

    let foo = F::new(x);
    foo.foo();
}

如果函数足够复杂,您可以拥有一个两个函数都调用的私有辅助函数:

// call with no type parameters
pub fn bar() {
    bar_inner(|_x| { /* do nothing */ })
}

// call with type parameter
pub fn bar_with<F: Foo>() {
    bar_inner(|x| {
        let foo = Foo::new(x);
        foo.foo();
    })
}

fn bar_inner<Func: FnOnce(u64)>(func: Func) {
    let x = some_computation();
    func(x);
}

或者,您可以使用针对所有 Foo 类型和默认类型 (()) 实施的附加特征来解决此问题:

trait BarArg {
    fn bar_inner(x: u64);
}

impl<F: Foo> BarArg for F {
    fn bar_inner(x: u64) {
        let foo = foo::new(x);
        foo.foo();
    }
}

impl BarArg for () {
    fn bar_inner(_x: u64) {
        // do nothing
    }
}

fn bar<B: BarArg>() {
    let x = some_computation();
    B::bar_inner(x)
}

我现在知道了! :D 我可以提供 Foo:

的 noop 实现
pub struct NoopFoo {}
impl Foo for NoopFoo {
    fn new(_: u64) -> Self { NoopFoo {} }
    fn foo(&self) {}
}

我仍然很好奇这是否可以用宏来解决...