简体   繁体   English

将特征实现到多种类型的问题

[英]Problem implementing a trait to multiple types

As a practice exercise, I'm trying to create a temperature unit library.作为练习,我正在尝试创建一个温度单位库。

Currently, the following code won't compile and the error message is unclear to me.目前,以下代码无法编译,并且我不清楚错误消息。 The problem seems to be a type conflict.问题似乎是类型冲突。

type Fahrenheit = f64;
type Celcius = f64;
type Kelvin = f64;


trait TemperatureUnit {
    fn to_kelvin(&self) -> Kelvin;
}

impl TemperatureUnit for Fahrenheit {
    fn to_kelvin(&self) -> Kelvin {
        (*self + 459.67) * 5/9
    }
}

impl TemperatureUnit for Celcius {
    fn to_kelvin(&self) -> Kelvin {
        *self + 273.15
    } 
}

impl TemperatureUnit for Kelvin {
    fn to_kelvin(&self) -> Kelvin {
        *self
    }
}

Errors:错误:

error[E0119]: conflicting implementations of trait `TemperatureUnit` for type `f64`
  --> src/lib.rs:18:1
   |
12 | impl TemperatureUnit for Fahrenheit {
   | ----------------------------------- first implementation here
...
18 | impl TemperatureUnit for Celcius {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `f64`

error[E0119]: conflicting implementations of trait `TemperatureUnit` for type `f64`
  --> src/lib.rs:24:1
   |
12 | impl TemperatureUnit for Fahrenheit {
   | ----------------------------------- first implementation here
...
24 | impl TemperatureUnit for Kelvin {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `f64`

Playground 操场

I'm new to Rust, so maybe this is not the proper methodology to achieve a compiler-enforced type-safe unit conversion.我是 Rust 的新手,所以也许这不是实现编译器强制类型安全单元转换的正确方法。

When you write type , you're making a type alias, not a new type.当你写type时,你是在创建一个类型别名,而不是一个新类型。 It's like when you use typedef in C++, for instance.例如,就像在 C++ 中使用typedef一样。

type Fahrenheit = f64;

This literally says "when I say 'Fahrenheit', pretend I said f64 instead".字面意思是“当我说'华氏'时,假装我说的f64 ”。 Not a new type, not a distinct structure, literally the same.不是一种新的类型,不是一种独特的结构,字面上是一样的。 So you just implemented TemperatureUnit for f64 three times.因此,您刚刚TemperatureUnit for f64

If you want to wrap f64 in a custom type under your control, you're looking for the newtype pattern .如果您想将f64包装在您控制下的自定义类型中,您正在寻找newtype pattern

pub struct Fahrenheit(pub f64);

Now Fahrenheit is a distinct structure that happens to be isomorphic to f64 .现在Fahrenheit是一个独特的结构,恰好与f64同构。 You can go from f64 to Fahrenheit with the Fahrenheit(x) constructor, and you can go back with the my_fahrenheit.0 projection.您可以使用Fahrenheit(x)构造函数从f64转到Fahrenheit ,也可以使用my_fahrenheit.0投影返回。 You can also name the projection if you prefer.如果您愿意,也可以命名投影。

pub struct Fahrenheit { pub float_repr: f64 };

Rust will almost certainly compile this internally to an actual f64 . Rust 几乎肯定会在内部将其编译为实际的f64 Structs with only one field will generally have no overhead.只有一个字段的结构通常没有开销。 But we can actually guarantee this with repr(transparent)但我们实际上可以用repr(transparent)来保证这一点

#[repr(transparent)]
pub struct Fahrenheit(pub f64);

This guarantees that, at runtime, Fahrenheit and f64 have the same representation (even to the extent that transmute is defined between them), while at compile-time they're distinct types.这保证了在运行时, Fahrenheitf64具有相同的表示形式(即使在它们之间定义了transmute的程度),而在编译时它们是不同的类型。

The only thing left to decide is what traits you want your new type to implement.唯一需要决定的是您希望新类型实现哪些特征。 It will inherit nothing from f64 by default, so you'll likely have a massive derive(...) clause right before the declaration including Eq , Ord , Clone , Copy , etc. (non-exhaustive list; you know your use case better than I do)默认情况下,它不会从f64继承任何内容,因此您可能会在声明之前有一个巨大的 derived derive(...)子句,包括EqOrdCloneCopy等(非详尽列表;您知道您的用例比我好)

The problem here is that type just aliases a type, it doesn't create a new one.这里的问题是type只是一个类型的别名,它不会创建一个新的类型。 Rust is complaining because those three impl blocks are trying to implement TemperatureUnit on the same type three times. Rust 抱怨是因为这三个impl块试图在同一类型上实现TemperatureUnit三次。

Instead, you should declare new types.相反,您应该声明新类型。 The example below uses struct .下面的示例使用struct

Rust playground 锈操场

struct Fahrenheit(f64);
struct Celcius(f64);
struct Kelvin(f64);


trait TemperatureUnit {
    fn to_kelvin(&self) -> Kelvin;
}

impl TemperatureUnit for Fahrenheit {
    fn to_kelvin(&self) -> Kelvin {
        Kelvin((self.0 + 459.67) * 5.0/9.0)
    }
}

impl TemperatureUnit for Celcius {
    fn to_kelvin(&self) -> Kelvin {
        Kelvin(self.0 + 273.15)
    } 
}

impl TemperatureUnit for Kelvin {
    fn to_kelvin(&self) -> Kelvin {
        Kelvin(self.0)
    }
}


fn main()
{
    println!("Hey");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM