简体   繁体   English

如何以及何时在 Rust 的 impl 块中使用泛型类型参数?

[英]How and when to use the generic type parameter in impl block in Rust?

I am confused about the generic parameter for the impl keyword in Rust.我对 Rust 中impl关键字的泛型参数感到困惑。

To explain what I am confused about, I am currently going through the rustling exercises.为了解释我的困惑,我目前正在进行沙沙练习。 I am doing the generics part.我正在做泛型部分。 The second question which can be seen here is to make the Wrapper struct generic. 这里可以看到的第二个问题是使Wrapper结构通用。

Easy, the solution is:很简单,解决方法是:

struct Wrapper<T> {
    value: T,
}

impl<T> Wrapper<T> {
    pub fn new(value: T) -> Self {
        Wrapper { value }
    }
}

But I ask myself, why does impl have to also have a type parameter?但是我问自己,为什么impl还必须有一个类型参数? Why impl<T> Wrapper<T> and not just impl Wrapper<T> ?为什么impl<T> Wrapper<T>而不仅仅是impl Wrapper<T>

I could not answer that question but on moving on to the next exercise, which can be seen here there is another generic question, which I solved by having this:我无法回答这个问题,但在继续下一个练习时,可以看到这里还有另一个通用问题,我通过以下方式解决了这个问题:

use std::fmt::Display;

pub struct ReportCard<T:Display> {
    pub grade: T,
    pub student_name: String,
    pub student_age: u8,
}

impl<T:Display> ReportCard<T> {
    pub fn print(&self) -> String {
        format!("{} ({}) - achieved a grade of {}",
            &self.student_name, &self.student_age, &self.grade.to_string())
    }
}

Now I am wondering, why is this right, and the following wrong:现在我想知道,为什么这是对的,而以下是错误的:

pub struct ReportCard<T:Display> {
    pub grade: T,
    pub student_name: String,
    pub student_age: u8,
}

impl<T:Display> ReportCard<T:Display> {
    pub fn print(&self) -> String {
        format!("{} ({}) - achieved a grade of {}",
            &self.student_name, &self.student_age, &self.grade.to_string())
    }
}

I think the crux of my problem is, I do not really understand the generics syntax.我认为我的问题的症结在于,我不太了解泛型语法。 And where to put the generic type parameter, especially when there is an impl block.以及在哪里放置泛型类型参数,特别是当有一个impl块时。

Does anyone have an explanation on how these syntax is supposed to be used?有没有人解释应该如何使用这些语法?

You could think of it the same way a function takes parameters, and can feed back these parameters in a function call, and it will probably make sense.你可以把它想象成函数接受参数的方式,并且可以在函数调用中反馈这些参数,这可能是有意义的。

A generic type Wrapper<T> is "like" a function that takes a type T , and produces an actual, concrete type Wrapper<T> (even though there is no difference in the syntax, just like there is no difference when you define a function and when you call it to get an actual value out of it).泛型类型Wrapper<T> “类似于”一个接受类型T并生成实际的具体类型Wrapper<T>的函数(即使语法没有区别,就像定义时没有区别一样)一个函数,当您调用它以从中获取实际值时)。

Similarly, an impl block is "like" a function that may take some parameters, and that produces a given implementation for a given type.类似地,一个impl块“就像”一个可以接受一些参数的函数,并为给定类型生成给定实现。 That is, impl<T> Wrapper<T> { ... } should be read like define a generic implementation that takes a parameter, T , and that produces an actual, concrete implementation { ... } for the concrete type Wrapper<T> , which, in this case, can be seen as the "function call" rather than the "signature declaration".也就是说, impl<T> Wrapper<T> { ... }应该被理解为定义一个通用实现,它接受一个参数T ,并为具体类型Wrapper<T>生成一个实际的具体实现{ ... } Wrapper<T> ,在这种情况下,可以将其视为“函数调用”而不是“签名声明”。

But, for instance, you could also choose not to make your implementation generic: impl Wrapper<u32> { ... } , or even to make it generic over several arguments, not use none in the Wrapper<T> "call": impl<T, U> Wrapper<char> { ... } because these type parameters can also be used in the actual implementation.但是,例如,您也可以选择不使您的实现通用: impl Wrapper<u32> { ... } ,或者甚至使其在多个参数上通用,不在Wrapper<T> “调用”中使用 none: impl<T, U> Wrapper<char> { ... }因为这些类型参数也可以在实际实现中使用。


This being said, we can now look at the syntax more in details.话虽如此,我们现在可以更详细地查看语法。 Usually, when you define a "generic block" (that is, a generic impl , or a generic struct , or a generic enum , or ...), the first keyword of that block may also specify some times with the <T, U, ...> syntax, which you may think as in a function definition.通常,当您定义“通用块”(即,通用impl或通用struct或通用enum或...)时,该块的第一个关键字也可能使用<T, U, ...>语法,您可能会将其视为函数定义。 Every subsequent usage of these types in the block through the same syntax may be though of as a "function call": you feed these arguments into an other "function".通过相同语法在块中对这些类型的每次后续使用都可能被视为“函数调用”:您将这些参数提供给另一个“函数”。

For this reason, you can only add constraints over these types in the first <...> part of an impl block, not after the type:出于这个原因,您只能在impl块的第一个<...>部分中添加对这些类型的约束,而不是在类型之后:

impl<T: Display> Wrapper<T> { ... }

is valid, because it means given a type T such that T implements the trait Display , I can provide an implementation for the concrete type Wrapper<T> , but是有效的,因为它意味着给定一个类型T使得T实现特征Display ,我可以提供具体类型Wrapper<T>的实现,但是

impl<T: Display> Wrapper<T: Display> { ... }

is not valid, and neither is无效,也不是

impl<T> Wrapper<T: Display> { ... }

because you are putting constrains on a "function call".因为您正在对“函数调用”施加约束。 Just like就像

fn something(a: usize) {
  something_else(a);
}

is valid, but not是有效的,但不是

fn something(a: usize) {
  something_else(a: usize)
}

nor也不

fn something(a) {
  something_else(a: usize)
}

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

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