rust 如果类型设置在左侧,为什么我会得到“范围内的多个适用项”?

h7appiyu  于 2023-05-18  发布在  其他
关注(0)|答案(1)|浏览(200)

示例代码:

struct Test<T>(T);

impl Test<(i32, i32)> {
    fn new(a: i32, b: i32) -> Self {
        Self((a, b))
    }
}

impl Test<(i32, i32, i32)> {
    fn new(a: i32, b: i32, c: i32) -> Self {
        Self((a, b, c))
    }
}

这段代码运行良好:

_ = Test::<(_, _)>::new(1, 2);

但为什么这行不通呢?

// multiple applicable items in scope
let _: Test<(_, _)> = Test::new(1, 2);

如果答案很复杂,我也想知道为什么要添加这些限制,为什么我不能直接写:

_ = Test::new(1, 2);
b09cbbtk

b09cbbtk1#

Test::new是不明确的。
Rust不支持 * 函数重载 * 这里通过在不同的impl块上使用相同的方法名可以获得类似的效果,但这不是一回事。你应该把它们看作是具有相同名称但在不同命名空间中的函数:Test::<(i32, i32)>Test::<(i32, i32, i32)>
Rust有一个强大的 * 类型推断 * 机制。这种区别在这种情况下很重要,因为虽然类型推断可以“看穿”泛型函数,但它需要解析为单个声明才能完成其工作。如果没有函数重载解析机制(能够根据参数推断应该使用什么重载),Rust编译器会找到Test::new的两个候选对象,并抛出一个错误,要求 you 消除它们的歧义。

_ = Test::<(_, _)>::new(1, 2);

所以在这种情况下,使用完全限定语法消除了函数的歧义(至少足够好),这意味着Test::<(i32, i32, i32)>不是候选者。因此,可以解决单个定义。例如,如果有一个Test::<(f64, f64)>::new的定义,它仍然是模糊的,需要进一步的限定。

let _: Test<(_, _)> = Test::new(1, 2);

这不起作用,因为虽然类型注解和参数严重暗示了特定的定义,但如果左侧知道它将推断到的函数声明并且编译器在解析函数时不考虑参数,则只能通过反向类型推断产生影响。

_ = Test::new(1, 2);

同上。
由于这些原因,对不同的impl块使用相同的函数名通常被降级为 methods(其中有一个self参数),以便部分解析(通常)已经完成。例如,请参见std::any::Any上的方法。
为什么不支持函数重载?
1.重载解析规则总是由于类型强制之间的交互而变得复杂,并且经常与用于选择最佳候选项的排名系统一起使用。
1.它不能很好地处理类型推断。参数计数重载(正如您在问题中所提出的)可能是可行的,但这并不是在语言中支持它的足够强大的动力。

  1. Rust可以通过类型推断和traits实现类似函数重载的功能。使用From::fromDefault::default是常见的。
    参见:

相关问题