I want to access self fields from struct dynamically generated by macro_rules!, but got an error:
error[E0308]: mismatched types
--> src/main.rs:102:45
|
102 | packet.write_u8(self.$field_name).unwrap();
| -------- ^^^^^^^^^^^^^^^^ expected `u8`, found struct `String`
| |
| arguments to this function are incorrect
...
117 | / packet! {
118 | | properties {
119 | | opcode 100;
120 | | size: u16;
... |
128 | | }
129 | | }
| |_____- in this macro invocation
|
note: associated function defined here
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/byteorder-1.4.3/src/io.rs:1098:8
|
1098 | fn write_u8(&mut self, n: u8) -> Result<()> {
| ^^^^^^^^
= note: this error originates in the macro `packet` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0308`.
Well, there's nothing wrong with accessing the field. Your type's name field is a String, though, so I'm not sure why you expect to be able to pass it to a method that takes u8.
Thinking about it, even the way you are trying to differentiate between field types doesn't make sense. You are matching on the stringification of the field type, and trying to dispatch to differently-typed writers on that. This can't possibly work: a field always has a given specific type, so if you have some code that uses it, all such code will see it as having that one type. And of course all of an expression has to compile without error in order for it to compile; match statements are no exception (match matches on runtime values of a specific type, not on different types), so you can't treat field as u8 in one branch and as a String in another.
What you probably wanted instead is implement a trait for all supported field types with a method that allows each such type to write a binary representation of itself into the passed buffer.
I am not sure if understand correctly where I should implement the trait. Should it be trait on global type ?
Like impl std::io::Write on String ? Or this should be the trait on struct generated by macro_rules! ?
If so how this trait should looks like ? Could you attach some small example ? (it doesn't necessary to provide sandbox, just to point me in correct direction).
I got it, thank you ! Could you also tell is such approach a common practice ? Or this one can lead to some bugs or undesired behavior ? I am not feel OK about extending standard types.
Note that this "extension" must be explicitly acknowledged by the user (by importing the trait to scope or using generics), unlike e.g. JavaScript, where changes to prototype are immediately visible everywhere.
probably that's because of JavaScript approaches (my first language and still one of most usable for now), where when you add something to prototype it became global. So I used to avoid such approaches
Probably it's possible to make trait local ? So if I define it in same file (or module) where macros defined will it still visible outside ?
P.S. I answered to wrong post, not sure how to change that. It's the answer to "Why?"
Well, everything that user can do with macros they can do without them (by essentially copying over the output of cargo expand and probably adjusting some parentheses). Point is, they will have to use your trait to see the extension - they will not blunder into it unexpectedly.
You don't need to. Methods on a trait never conflict with any other method of any other trait. If two traits currently in scope define a method with the same name, and you try to call it, you get a compile error, so you can't call the wrong method by accident. And if your trait method has the same name as an inherent method, then the inherent method takes precedence, so you can't surprise-override what e.g. String::len() does. It also doesn't affect any code that doesn't explicitly bring the trait in scope, so you can rest assured that a new trait you created won't mess up any 3rd-party code – all 3rd-party code will be ignorant of your own trait unless it explicitly opts for using it.
Rust is an exceptionally well-designed language, unlike JavaScript. It's strongly typed, and the designers take a lot of care not to introduce footguns like this. You basically don't have to worry about this kind of problem unless you get an explicit compiler error.
It's very common to define a trait which will be implemented on foreign types to facilitate certain behaviour.
Think of it like how other languages let you overload functions, letting you invoke different implementations depending on what type of object you pass to it. Rust has a much stronger type system than JavaScript, so there is no ambiguity or way to accidentally shoot yourself in the foot.
Please share actual code. It's very likely that you are not in fact use-ing the trait, or maybe you forgot to implement it for u32. If the type implements the trait, and the trait is in scope, you definitely can call it.
I think I got what's happened. Seems like I need to bring the trait into each file where I want to use the macro. Could you tell does it possible to bring trait globally into scope ? To avoid import it everywhere. I expected that I can import it only in macro file.