Should trait bounds be duplicated in struct and im

2019-02-16 11:53发布

The following code uses a struct with generic type. While it's implementation is only valid for the given trait bound, the struct can be defined with or without the same bound. The struct's fields are private so no other code could create an instance anyway.

trait Trait {
    fn foo(&self);
}

struct Object<T: Trait> {
    value: T,
}

impl<T: Trait> Object<T> {
    fn bar(object: Object<T>) {
        object.value.foo();
    }
}

Should the trait bound for the structure should be omitted to conform to the DRY principle, or should it be given to clarify the dependency? Or are there circumstances one solution should be preferred over the other?

标签: rust
2条回答
Bombasti
2楼-- · 2019-02-16 12:14

Trait bounds that apply to every instance of the struct should be applied to the struct:

struct IteratorThing<I>
where
    I: Iterator,
{
    a: I,
    b: Option<I::Item>,
}

Trait bounds that only apply to certain instances should only be applied to the impl block they pertain to:

struct Pair<T> {
    a: T,
    b: T,
}

impl<T> Pair<T>
where
    T: std::ops::Add<T, Output = T>,
{
    fn sum(self) -> T {
        self.a + self.b
    }
}

impl<T> Pair<T>
where
    T: std::ops::Mul<T, Output = T>,
{
    fn product(self) -> T {
        self.a * self.b
    }
}

to conform to the DRY principle

The redundancy will be removed by RFC 2089:

Eliminate the need for “redundant” bounds on functions and impls where those bounds can be inferred from the input types and other trait bounds. For example, in this simple program, the impl would no longer require a bound, because it can be inferred from the Foo<T> type:

struct Foo<T: Debug> { .. }
impl<T: Debug> Foo<T> {
  //    ^^^^^ this bound is redundant
  ...
}
查看更多
Emotional °昔
3楼-- · 2019-02-16 12:30

It really depends on what the type is for. If it is only intended to hold values which implement the trait, then yes, it should have the trait bound e.g.

trait Child {
    fn name(&self);
}

struct School<T: Child> {
    pupil: T,
}

impl<T: Child> School<T> {
    fn role_call(&self) -> bool {
        // check everyone is here
    }
}

In this example, only children are allowed in the school so we have the bound on the struct.

If the struct is intended to hold any value but you want to offer extra behaviour when the trait is implemented, then no, the bound shouldn't be on the struct e.g.

trait GoldCustomer {
    fn get_store_points(&self) -> i32;
}

struct Store<T> {
    customer: T,
}

impl<T: GoldCustomer> Store {
    fn choose_reward(customer: T) {
        // Do something with the store points
    }
}

In this example, not all customers are gold customers and it doesn't make sense to have the bound on the struct.

查看更多
登录 后发表回答