简体   繁体   English

在结构中使用泛型并通过特征实现

[英]Using a generic in a struct and implementing via a trait

I have struct defined like this我有这样定义的结构

struct Shape<T> {
    line_one: T,
    line_two: T,
}

I am trying to create a simple trait and implementation that takes this struct to calculate some simple math.我正在尝试创建一个简单的特征和实现,它使用这个结构来计算一些简单的数学。

My trait and impls look like this我的 trait 和 impls 看起来像这样

trait Calculate<T: Mul<Output = T>> {
    fn calc(&self, Shape: T) -> T;
}

impl<T> Calculate<T> for Shape<T> {
    fn calc(&self, Shape: T) -> T {
        self.line_one * 2 + self.line_two * 2
    }
}

impl Calculate<i32> {
    fn calc(&self) -> i32 {
        self.line_one * 2 + self.line_two * 2
    }
}

fn calc_fn<T: Calculate<i32>>(calculate: T) {
    calculate.calc();
}

When I put this into the Rust playground, the compile fails as Mul is not implemented in the impl Calculate<T> .当我把它放到 Rust 的操场上时,编译失败,因为Mul没有在impl Calculate<T> However, if I change <T> to <T: Mul<Output = T>> , I get the error that但是,如果我将<T>更改为<T: Mul<Output = T>> ,则会出现错误

<anon>:14:21: 14:22 error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `:`
<anon>:14 impl <T> Calculate<T: Mul<Output = T>> for Shape<T> 

I'm at a loss as how to implement Mul for T in impl Calculate<T> .我不知道如何在impl Calculate<T>T实现Mul

What I'm trying to achieve is code that I can send either a f32 or i32 in without needing to create two different impl definitions so can just pass in T .我想要实现的是我可以发送f32i32 ,而无需创建两个不同的 impl 定义,因此只需传入T

This syntax is incorrect:此语法不正确:

impl<T> Calculate<T: Mul<Output = T>> for Shape<T>

you want你要

impl<T: Mul<Output = T>> Calculate<T> for Shape<T>

I'd almost always recommend using where clause instead;我几乎总是建议使用where子句; I think it reads better and might have helped prevent this case:我认为它读起来更好,可能有助于防止这种情况:

impl<T> Calculate<T> for Shape<T>
    where T: Mul<Output = T>,

This just unlocks more errors.这只会解锁更多错误。 You are trying to multiply by 2 , an integral variable that hasn't been fully nailed down yet — is it a u8 ?您正在尝试乘以2 ,这是一个尚未完全确定的整数变量 - 是u8吗? is it an i32 ?i32吗? The restrictions don't say that T can be multiplied by a 2 .限制并不是说T可以乘以2 Additionally, you try to add values together, but there's no guarantee that you can add the type T .此外,您尝试将值添加在一起,但不能保证您可以添加类型T

The impl Calculate<i32> and calc_fn blocks don't really make sense; impl Calculate<i32>calc_fn块没有意义; it's unlikely you want to implement functions for the trait and you don't supply a Shape in the latter.您不太可能想要为特征实现函数,并且您不会在后者中提供Shape The trait also doesn't use the Shape: T parameter (and Rust uses snake_case variable names anyway).该 trait 也不使用Shape: T参数(并且 Rust 无论如何都使用snake_case变量名)。

What I'm trying to achieve is code that I can send either a f32 or i32 in without needing to create two different impl definitions so can just pass in T .我想要实现的是我可以发送f32i32 ,而无需创建两个不同的 impl 定义,因此只需传入T

In this case, it'd probably be way easier:在这种情况下,它可能会更容易:

trait Calculate<T> {
    fn calc(&self) -> T;
}

impl Calculate<i32> for Shape<i32> {
    fn calc(&self) -> i32 {
        self.line_one * 2 + self.line_two * 2
    }
}

impl Calculate<f32> for Shape<f32> {
    fn calc(&self) -> f32 {
        self.line_one * 2.0 + self.line_two * 2.0
    }
}

Note that these are not equivalent!请注意,这些不是等价的! The f64 has floating point literals ( 2.0 ) not integral ( 2 ). f64具有浮点文字( 2.0 )而不是整数( 2 )。

If you must have a generic, you are going to need to convert the literal 2 to the type T or add a restriction that T can be multiplied by some known size.如果您必须有一个泛型,您将需要将文字2转换为类型T或添加一个限制,即 T 可以乘以某个已知大小。 Unfortunately, f64 cannot by multiplied by any integral value without casting.不幸的是, f64不能在没有强制转换的情况下乘以任何整数值。

The FromPrimitive trait from the num crate is key here.来自 num crate 的FromPrimitive特性是这里的关键。 Making the types Copy also makes the implementation easier.使类型Copy也使实现更容易。

extern crate num;

use std::ops::{Add, Mul};
use num::FromPrimitive;

struct Shape<T> {
    line_one: T,
    line_two: T,
}

trait Calculate<T> {
    fn calc(&self) -> T;
}

impl<T> Calculate<T> for Shape<T>
    where T: Copy + FromPrimitive + Add<Output = T> + Mul<Output = T>,
{
    fn calc(&self) -> T {
        let two = T::from_u8(2).expect("Unable to create a value of two");
        self.line_one * two + self.line_two * two
    }
}

fn main() {
    let s1 = Shape { line_one: 2, line_two: 3 };
    let s2 = Shape { line_one: 2.0, line_two: 3.0 };

    println!("{}", s1.calc());
    println!("{}", s2.calc());
}

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

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