This does not mean that the type parameter to print is going to be the struct type SomeOptions; it just means you have a completely arbitrary type parameter which happens to be called SomeOptions. This is absolutely equivalent to:
There's no reason to expect a CouldBeAnything to have a field called option, so of course it doesn't work. What's more, this isn't even compatible with the trait you're trying to implement. The trait specifies that the first type parameter is bound by the trait Option.
When you're dealing with generic type parameters in Rust, you can only perform operations supported by the listed bounds. So, in this case, you need to use a method defined by Option. I don't know what exactly you're trying to do here, but this is one way you could do it:
(Aside: you should generally avoid .to_string() for constructing Strings; it's inefficient as it goes through the formatting system instead of just directly constructing the result.)
the type of the paramter options is explicit given and the compiler can check whether the type has a field option.
If I use CouldBeAnything and CouldBeAnything has no field option then the compiler should stop with this error...
...so I still do not understand why this is an error...?
No, it isn't. You've just written the code in such a way that it looks like it has.
The <SomeOptions> part is introducing a generic type parameter, whose name is shadowing the actual SomeOptions struct type. You absolutely cannot, in this context, require that options is actually of type SomeOptions.
As I said above, that name is just confusing matters. Change it to T or CouldBeAnything; it is not doing what you think it is.
Then, as I said, you need to add something to the trait to access the field. You can't accept a generic type, and then do something that depends on knowing what the type is.
Because, in this specific case, optionsreally is a SomeOptions, and the compiler can prove it.
In your second example you've specified an associated type. Here you tell the compiler explicitly that the type is SomeOptions.
Think about it this way: You can implement the trait for any type: ints, floats, structs, etc. And not all of them do have fields like a struct has. But you can define functions (methods) for all of them. And this is what the compiler can guarantee: that you have defined all the functions that are demanded by a specific trait.
Okay it makes sense that the type is not "explicit given" respectively that the compiler only cares about the trait definition ... because I can write this:
fn do_print<T: OptionPrinter, O: Options>(printer: T, options: O) {
printer.print(options);
}
fn main() {
let printer = SomeOptionPrinter;
let options = CouldBeAnything; // ... that impl Options
do_print(printer, options);
}