Understanding trait implementation and trait bound

I think I am running into two issues understanding impl Trait to a struct and passing a function parameter with certain Trait bounds.

I have several structs that derives Serialize, Debug, Default. I also created a Trait TypeOf to determine the type of struct:

#[derive(Serialize, Debug, Default)]
pub struct TriggerOutput {
    id: String,
    name: String,
    data_type: String,
}

impl TypeOf for TriggerOutput {
    fn type_of(&self) -> &'static str {
        "trigger"
    }
}

Now, I am pushing data of type TriggerOutput to a Vec, and then passing the Vec to a function:

pub fn to_csv<S: TypeOf + Serialize + std::fmt::Debug>(&self, game_object: S) {
            let mut path = String::new();
            path.push_str(&self.iop());
            path.push_str("_");
            path.push_str(game_object.type_of());
            path.push_str(".csv");

            //let mut csv_writer = Writer::from_path(Path::new(&path)).unwrap();

            println!("PATH = {:?}\nOBJECT = {:?}", path, &game_object);
 }

The compiler will recognize and pass the TriggerOutput struct to pass to the function. But, it will not allow a Vec to pass. It wants me to impl TypeOf for Vec. I'm a little confused. Because TriggerOutput is the type and implements TypeOf, shouldn't that implementation be passed to the Vec also?

When I impl TypeOf for Vec and try to iterate through the elements of the Vec, it says 'S' is not an iterator. Yet, 'S' is a Vec. Shouldn't a Vec automatically impl IntoIterator?

So, I add the ' + std::iter::Iterator' trait bound to the function signature:

pub fn to_csv<S: TypeOf + Serialize + std::fmt::Debug + std::iter::Iterator>(
            &self,
            game_object: S,
        ) 

The compiler errors for
<S as Iterator>::Item` doesn't implement `Debug
Now, I am totally lost and confused....my function has a trait bound for Debug and my TriggerOutput derives Debug. Isn't the item of Vec, a Trigger Output? And, TriggerOutput derives Debug......Totally confused here.

No, that's not how traits work. If the type_of method does not use the data in self, perhaps you rather want it as an associated function on the trait (doesn't have &self in the signature), and you can then call it as S::type_of().

I am a little confused about what you're trying to accomplish, but if you want to pass a Vec to the to_csv function, perhaps you should actually put the Vec in the function signature, like game_object: Vec<S> instead of S directly.

1 Like

Only when you call the function with a Vec<TriggerOutput>. Generic functions have to be valid for all inputs that satisfy the bounds, and currently the bounds do not require anything about the items, so it should be valid to call it with an iterator where the items are of type SomethingThatIsntDebug. But you're relying on the items being Debug in the function body, hence a compilation error. The Rust compiler will not check what you're doing inside the function and add bounds implicitly the way you expect. This is useful to avoid accidentally breaking backwards compatibility for example, you cannot accidentally start relying on some trait being implemented without changing the signature.

I had already tried game_object: Vec<S> and the compiler responded with no method named `type_of` found for struct `Vec<S>` in the current scope

What I am trying to accomplish is to pass a Vec<S> where S impl Serialize + TypeOf. I have three structs that meet those bounds that I need to pass.

That sounds like you're trying to call type_of() on the Vec itself. You want to call type_of() on each element of the Vec, or have an implementation of TypeOf for Vec<S> whenever S implements TypeOf.

No.

Actually, why would it? Why would you expect traits to work like that? There are tons of properties that don't automatically apply to collections of elements, even though they apply to the elements themselves.

For example, there's no obviously correct way to compare an unordered HashMap or HashSet for less/greater inequality, even though its elements may well be comaprable. There's no obviously good way to Display a vector even though its elements may be Displayable. There's no obvious way to Borrow the inner value of a Mutex, even though its inner value might be Borrow. And the list goes on.

If you want a trait to be implemented for a container type, you have to implement it, by considering how (and if at all) it can/should be done in a meaningful way.

Ok, this is probably where I am stuck....
I need to get the underlying item type for the Vec. The Vec can only be of three struct types: TriggerOutput, BlobOutput, or AlertOutput. Each of these impl my TypeOf:

pub trait TypeOf {
        fn type_of(&self) -> &'static str;
    }

I also have these implementations for each Vec:

impl TypeOf for Vec<TriggerOutput> {
    fn type_of(&self) -> &'static str {
        "triggers"
    }
}

There's no obviously good way to Display a vector even though its elements may be Display able. There's no obvious way to Borrow the inner value of a Mutex , even though its inner value might be Borrow .

I think this answers my biggest underlying question.....even if I implement a trait to an element, does the container also inherit those traits or need separate implementation.

It needs a separate implementation. When you write impl MyTrait for MyType, it implements the trait for exactly that type, and not any other types.

For example, consider this trait:

trait Person {
    fn age(&self) -> u32;
}

What would you expect .age() to return if you call it on a Vec<P> instead of a single P: Person value?

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.