I am looking to create a generic set of methods to be used on various types of structs using traits.
However, when I try to access inner properties of said structs within the trait implementation, it breaks.
Your trait Test has no way of knowing whether the implementor (MyStruct) has in fact a field inner. Rust's type system does not allow duck-typing like that. You have to give Test an explicit interface through which it can access the inner value in MyStruct and implement that interface accordingly. This could be a second method on Test which returns the value of the inner field for example, or we could add a supertrait, like this for example:
Not the trait Test<T>: Inner<T> line. It basically says that all types that implement Test<T> must also implement Inner<T>. This allows Test<T> to know that MyStruct has a method called inner, allowing it to access this method, as it is guaranteed by Rust that MyStruct definitely has this method. The same can't be said about the MyStruct.inner field, which could be there or not, the trait has no way of knowing that. Unfortunately there are no bounds we could add to Test that would say "this type must have the inner field", so we have to use a supertrait instead (or like I said above, add the inner() method directly to the Test trait and implement it for MyStruct accordingly.
Here an example of adding a second method to the Test trait, as it is easier than using a supertrait:
Thank you for the quick answer!
That makes sense, I think for my usecase implementing a supertrait or the other method you described might be overkill, so I will stick to duplicating code for now!
Are you sure? I find adding an inner method to Test rather convenient. You just have to implement it for each type that requires to have the Test trait. You'd still get the benefits of less code, because you don't have to implement test_fn for each type. I don't know why I started with the more complex example of supertraits, that was my bad.
I generally prefer the supertrait version if none of the types will need to override the default implementations, because then I can define a blanket implementation
impl<U, T: Inner<U>> Test<U> for T {}
This means that each type only needs one impl block, but it will have a different name than the trait you're usually going to be using.
I generally agree. Sometimes, however, this gets a bit messy when you want the inner contents to be private. In that case, you could have a supertrait in a private module, but overall it's quickly getting complex and documentation becomes weird in that case.
I'm not sure who you meant by "they", but anyone can write an RFC. I wouldn't get my hopes up for any particular RFC until it's accepted (and not all accepted RFCs stabilize, and some take years to stabilize).