预期的类型参数,找到u8,但类型参数是u8

时间:2016-05-24 10:17:56

标签: types compiler-errors rust type-inference

trait Foo {
    fn foo<T>(&self) -> T;
}

struct Bar {
    b: u8,
}

impl Foo for Bar {
    fn foo<u8>(&self) -> u8 {
        self.b
    }
}

fn main() {
    let bar = Bar {
        b: 2,
    };
    println!("{:?}", bar.foo());
}

Playground

上述代码导致以下错误:

error[E0308]: mismatched types
  --> <anon>:11:9
   |
11 |         self.b
   |         ^^^^^^ expected type parameter, found u8
   |
   = note: expected type `u8` (type parameter)
              found type `u8` (u8)

我的猜测是,问题来自特质中的泛型函数。

2 个答案:

答案 0 :(得分:8)

以下代码没有达到预期效果

impl Foo for Bar {
    fn foo<u8>(&self) -> u8 {
        self.b
    }
}

它引入了一个名为u8的泛型类型,它隐藏了具体类型u8。您的功能将与

完全相同
impl Foo for Bar {
    fn foo<T>(&self) -> T {
        self.b
    }
}

在这种情况下无法正常工作,因为T来电者所选择的foo并不能保证为u8

要解决此问题,请选择与具体类型名称不冲突的泛型类型名称。请记住,实现中的函数签名具有以匹配特征定义中的签名。

要解决所提出的问题,您希望将通用类型修复作为特定值,您可以将通用参数移动到特征,并为u8实现特征:

trait Foo<T> {
    fn foo(&self) -> T;
}

struct Bar {
    b: u8,
}

impl Foo<u8> for Bar {
    fn foo(&self) -> u8 {
        self.b
    }
}

或者你可以使用相关的特性,如果你不想要特定类型的多个Foo impl(感谢@MatthieuM):

trait Foo {
    type T;
    fn foo(&self) -> T;
}

struct Bar {
    b: u8,
}

impl Foo for Bar {
    type T = u8;
    fn foo(&self) -> u8 {
        self.b
    }
}

答案 1 :(得分:2)

让我们看一个稍微更一般的例子。我们将定义一个带有函数的特征,该函数接受并返回泛型类型:

trait Foo {
    fn foo<T>(&self, value: T) -> T;
}

struct Bar;

impl Foo for Bar {
    fn foo<u8>(&self, value: u8) -> u8 {
        value
    }

    // Equivalent to 
    // fn foo<T>(&self, value: T) -> T {
    //    value
    // }
}

作为oli_obk - ker has already explainedfn foo<u8>(&self, value: u8) -> u8定义名为u8泛型类型参数,它会隐藏内置类型u8。出于兼容性原因,这是允许的 - 如果您决定调用泛型类型Fuzzy,然后一个包(或标准库!)引入了一个名为Fuzzy的类型,该怎么办?如果不允许阴影,您的代码将停止编译!

但是,你应该避免使用现有类型的泛型类型参数 - 如图所示,它只是令人困惑。

很多时候,人们会陷入这个陷阱,因为他们试图为通用参数指定具体类型。这表明存在对泛型类型如何工作的误解:泛函类型由函数的调用者选择;实施并没有选择它们是什么!

other answer中列出的解决方案消除了调用者选择通用的能力。

  • 将泛型类型移动到特征并且只针对少数类型实现它意味着只有一小部分实现可用。如果调用者试图使用没有相应实现的类型,那么它将无法编译。

  • 选择关联类型 Designed 以允许特征的实现者选择类型,在这种情况下通常是正确的解决方案。

有关在两者之间进行选择的详细信息,请参阅When is it appropriate to use an associated type versus a generic type?

这个问题对于学习Rust的人来说更为常见,因为他们还没有内化泛型的各种语法。快速复习......

功能/方法:

fn foo<T>(a: T) -> T
//    ^-^ declares a generic type parameter

fn foo<T>(a: T) -> T
//           ^     ^ a type, which can use a previously declared parameter

性状:

trait Foo<T>
//       ^-^ declares a generic type parameter

结构/枚举:

enum Wuuf<T>
//       ^-^ declares a generic type parameter

struct Quux<T>
//         ^-^ declares a generic type parameter

实现:

impl<T> Foo<T> for Bar<T>
//  ^-^ declares a generic type parameter

impl<T> Foo<T> for Bar<T>
//      ^----^     ^----^ a type, which can use a previously declared parameter

在这些示例中,只允许类型指定具体类型,这就是impl Foo<u8> for Bar有意义的原因。您可以通过在特征上声明一个名为u8的泛型来恢复原始状态!

impl<u8> Foo<u8> for Bar // Right back where we started

还有另一种罕见的情况:你并不是说这种类型在任何时候都是通用的!如果是这种情况,请重命名或删除泛型类型声明并编写标准函数。对于我们的示例,如果我们一直想要返回u8,无论传入的是什么类型,我们只需写下:

trait Foo {
    fn foo<T>(&self, value: T) -> u8;
}

impl Foo for Bar {
    fn foo<T>(&self, value: T) -> u8 {
        42
    }
}
好消息!至少从Rust 1.17开始,由于警告,引入这种类型的错误更容易发现:

warning: type parameter `u8` should have a camel case name such as `U8`