简体   繁体   English

为什么在具有泛型类型参数的结构定义中使用特征边界?

[英]Why use trait bounds in struct definitions with generic type parameters?

I can define a struct type that uses a generic type parameter with a trait bound:我可以定义一个结构类型,它使用具有特征绑定的泛型类型参数:

struct MyStruct<T: Clone> {
    field: T,
}

This prevents me me from instantiating MyStruct with a generic type which does not meet the trait bound:这使我无法使用不符合特征界限的泛型实例化MyStruct

// Note: does not implement Clone
struct UnitStruct;

fn main() {
    // ERROR: Unsatisfied trait bound: UnitStruct: Clone
    let s = MyStruct { field: UnitStruct };
}

But why would I want to define my struct this way?但是为什么我要这样定义我的结构呢? What are the use cases of imposing such limitations on the instantiation of MyStruct ?MyStruct的实例化施加此类限制的用例是什么?

I noticed that even with the trait bound in the MyStruct definition, if I define an interface which uses MyStruct , I still have to repeat the trait bound:我注意到即使在MyStruct定义中使用特征绑定,如果我定义一个使用MyStruct的接口,我仍然必须重复特征绑定:

// This works
fn func<T: Clone>(s: MyStruct<T>) -> T { s.field.clone() }

// This does not. Compiler demands a trait bound for `T`
fn func<T>(s: MyStruct<T>) -> T { s.field.clone() }

Typically you want to avoid adding trait bounds to structs when possible.通常,您希望尽可能避免向结构添加特征边界。 When you add trait bounds this way, you will have to write out the trait bounds again on every impl<T: A + B + C + Etc> for Foo<T> .当您以这种方式添加特征边界时,您将不得不在impl<T: A + B + C + Etc> for Foo<T>上再次写出特征边界。 This is more work for you and can even reduce readability if not all of those traits are actually necessary for the impl block you are writing.这对您来说是更多的工作,如果不是所有这些特征对于您正在编写的impl块实际上都是必需的,甚至会降低可读性。 Traits like Clone that do not have associated types or static functions probably shouldn't be included in the type bounds unless you have a specific reason to do so.Clone这样没有关联类型的特征或 static 函数可能不应该包含在类型范围中,除非您有特定的理由这样做。

The reason you can add trait bounds this way is because it can greatly improve readability and there are some cases where it is largely unavoidable such as with associated types or fields which must be Sized .您可以以这种方式添加特征边界的原因是因为它可以大大提高可读性,并且在某些情况下它在很大程度上是不可避免的,例如关联类型或必须为Sized的字段。

use std::sync::mspc::Receiver;

trait FooProcessor {
    type Input: Sized + Send;
    type Output: Sized;

    fn do_foo(&mut self, input: Self::Input) -> Self::Output;
}


struct FooHandler<P: FooProcessor> {
    processor: P,
    input_channel: Receiver<P::Input>,
    outputs: Vec<P::Output>,
}

We can avoid this by expanding to add more type parameters, but that gets messy quickly.我们可以通过扩展以添加更多类型参数来避免这种情况,但这很快就会变得混乱。 This can greatly reduce code readability and it gets worse if you have multiple nested generic structs.这会大大降低代码的可读性,如果您有多个嵌套的通用结构,情况会变得更糟。

struct FooHandler<P, I, O> {
    processor: P,
    input_channel: Receiver<I>,
    outputs: Vec<O>,
}

Another very important reason is to avoid mistakes.另一个很重要的原因是避免错误。 If it doesn't make sense for a type to be in a generic field, then you can get the compiler to catch it early by enforcing it on the struct parameters.如果类型在泛型字段中没有意义,那么您可以通过在结构参数上强制执行它来让编译器及早捕获它。 It is not a good feeling to have written your code around the idea of using a Foo<Bar> only to discover that Bar was never actually a valid option.围绕使用Foo<Bar>的想法编写代码只是为了发现Bar实际上从来不是一个有效的选项,这感觉不是很好。

The trait bound limits what type can be used in generic functions or structs, but also enables the generic T to do things it otherwise cannot. trait bound 限制了可以在泛型函数或结构中使用的类型,但也使泛型 T 能够做它本来不能做的事情。

For example例如

/// This function doesn't compile, because T cannot be cloned
fn clone_and_return1<T>(t: &T) -> T {
    t.clone()
}

/// This compiles, because we limit T to types that implements Clone
fn clone_and_return2<T>(t: &T) -> T
where
    T: Clone,
{
    t.clone()
}

So add trait bounds when you need T to have certain behavior.因此,当您需要 T 具有某些行为时,请添加特征界限。

If you need MyStruct to be Clone , then everything it owns must also be Clone, so T: Clone would make sense.如果您需要MyStructClone ,那么它拥有的所有东西也必须是 Clone,所以T: Clone才有意义。

However, if that's not required, it's best to not include such trait bound.但是,如果不需要,最好不要包含此类特征限制。

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

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