有没有办法提示编译器在使用 Option::None 时使用某种默认泛型类型?

Is there a way to hint to the compiler to use some kind of default generic type when using Option::None?

我需要一个函数来获取实现特征 std::iter::IntoIterator 的通用类型 TOption。 一个天真的实现可能如下所示(是的,解包会在 None 上出现恐慌):

fn main() {
    let v = vec![1i32, 2, 3];
    print_iter(Some(v));
    print_iter(None);
}

fn print_iter<T: IntoIterator<Item = i32>>(v: Option<T>) {
    for e in v.unwrap() {
        println!("{}", e);
    }
}

测试playground

这对 Some(...) 按预期工作,但对 None 失败:

error[E0282]: type annotations needed
 --> src/main.rs:4:5
  |
4 |     print_iter(None);
  |     ^^^^^^^^^^ cannot infer type for `T`

显然在那些情况下 T 的类型是未知的。可以使用 print_iter::<Vec<i32>>(None); 但这感觉并不是真正的惯用,因为它给出了一些不基于任何东西的任意类型...

有什么方法可以提示编译器我不关心 None 或使用某种默认值吗?

None没有问题。

当无法推断您的变量类型时,这只是一个问题,这通常不会发生。唯一有问题的情况是当您直接传递 None 并且没有类型化变量时。

您可以使用涡轮鱼指定类型:

print_iter::<Vec<i32>>(None);

但您通常不需要;你的正常情况宁愿是这样的:

let a: Option<Vec<i32>> = None;
print_iter(a);

print_iter(my_options.a);

并且两个构造都没有问题。

现在(在问题编辑之后),如果你真的希望能够通过 None 而不精确化类型,例如作为文字标志,那么你可以定义一个宏:

macro_rules! pi {
    (None) => {
         // here we handle the call as a nop but
         //  other behaviors are possible, like
         //  calling the function with a type
         //  specified with a turbofish
    };
    ($a:expr) => {
        print_iter($a)
    };
}

fn main() {
    let v = vec![1i32, 2, 3];
    pi!(Some(v));
    pi!(None);
}

fn print_iter<T: IntoIterator<Item = i32>>(v: Option<T>) {
    for e in v.unwrap() {
        println!("{}", e);
    }
}

Is there any way to hint to the compiler that I don't care for None or use some kind of default?

您可以实现自己的非通用值作为默认值。对于初学者,我们假设 print_iter 不接受 Option<T>,而是接受它自己的枚举:

enum PrintArg<T> {
    Ignore,
    Use(T),
}

fn print_iter<T: IntoIterator<Item = i32>>(v: PrintArg<T>) {
    if let PrintArg::Use(v) = v {
        for e in v {
            println!("{}", e);
        }
    }
}

这还没有解决问题,因为如果你将 PrintArg::Ignore 传递给 print_iter(),你又回到了原点——编译器无法推断出 T .但是使用您自己的类型,您可以轻松更改 print_iter 以接受任何可以 converted into PrintArg:

fn print_iter<T, V>(v: T)
where
    T: Into<PrintArg<V>>,
    V: IntoIterator<Item = i32>,
{
    if let PrintArg::Use(v) = v.into() {
        for e in v {
            println!("{}", e);
        }
    }
}

通过此修改,您可以创建一个虚拟的非泛型 Ignore 值并使用 From 特征定义其到 PrintArg::Ignore<T>T 的转换您的选择 - 例如:

struct Ignore;

impl From<Ignore> for PrintArg<Vec<i32>> {
    fn from(_v: Ignore) -> Self {
        PrintArg::Ignore
    }
}

由于 Ignore 是非通用的,因此它的使用不需要(或接受)<T>。虽然我们确实必须在 From 特征实现中为 PrintArg<T> 发明一种类型,但我们从未构造它,所以我们选择哪个类型无关紧要,只要它满足 IntoIterator 界限。

当然,您仍然希望能够使用 Some(...) 调用 print_iter(),因此您还将定义 Option<T>PrintArg<T> 的转换:

impl<T> From<Option<T>> for PrintArg<T> {
    fn from(v: Option<T>) -> Self {
        match v {
            Some(v) => PrintArg::Use(v),
            None => PrintArg::Ignore,
        }
    }
}

有了这些,你的 API 就干净了,让 main() 看起来像这样 (playground):

fn main() {
    let v = vec![1i32, 2, 3];
    print_iter(Some(v));
    print_iter(Ignore);
}