struct BaseMaterial {
sentense: String,
word: String,
//more fields need accessed in trait 'Say'
...
}
trait Say {
fn say(&self) {
self.translate(self.get_sentense());
self.translate(self.get_word());
}
//getter method
fn get_sentense(&self)-> &str;
fn get_word(&self)-> &str;
// more getter method
...
// virtual function need be implement by sub-type
fn translate<'a> (&self, original: &'a str)-> &'a str;
}
struct SayEnglish {
material: BaseMaterial,
...
}
impl Say for SayEnglish {
fn get_sentense(&self)-> &str {
&self.material.sentense
}
fn get_word(&self)-> &str {
&self.material.word
}
//more bridge method
...
// implement virtual function
fn translate<'a> (&self, original: &'a str)-> &'a str {
//translate
original
}
}
But these code looks ugly because BaseMaterial and trait Say are coupling especially there are many fields need to access in trait. Is there a better way?
I am a newbie, in my opinon, 'BaseMaterial' and 'Say' should almost be one structure which like virtual class in C++. But I want to know how to implement this more gracefully in rust. Please help, thanks
If the RFC hasn't been touched in two years, that should tell you all you need to know.
A trait is not an interface. If you need access to a given field, you should consider using a getter method (which can be present in a trait). This is not only more idiomatic, but avoids needlessly exposing internal details.
Thanks for reply.
I should have used getter method in the demo code, but the code looks ugly especially when there are many field need to access. please inspect my code and suggest a better method, thanks.
What do you mean by "far too ugly"? If you're concerned about the overhead of writing getters, you could almost certainly write a proc macro to do it for you.
Right now, you're trying to use a trait as an interface. Don't. You can define a trait that provided access to that field and have it be a supertrait to whatever you're trying to do. Beyond that and I'll need some actual code.
Basically, the code you've provided simply doesn't show the problem you're running into. With what you've shown, there is no reason a visibility modifier wouldn't suffice.
Thanks for reply.
I said that the code is ugly because when there are many BaseClass fields that need to be accessed in SomeTrait's method , BaseClass and SomeTrait are tightly coupled. In addition, all these access requirements need to be bridged by WorkClass.
From another point of view, BaseClass and SomeTrait should almost be in the one structure where its method can directly access its fields without being transmitted by WorkClass.
The struct names signals that you're trying to implement Java-style OO hierarchy, which doesn't works well with Rust. Neither struct nor trait are class. Traits are abstract behavior shared between types, and structs holds some state as its fields. Classes tends to mix both roles, sometimes more other roles too.
If the operation is tightly coupled with state, it's not a good idea to abstract it as a trait. Instead, you can make a top level struct which holds abstracted extensions.
To give a perhaps more satisfying answer in addition to the previous, correct, albeit somewhat philosophical ones:
In Rust, data and behavior are separate, and the layout of every type is exactly and statically determined at compile time. You define fields in your struct once, and they stay there forever. This is better for both readability, safety, and performance, since the definition of a type isn't scattered all over the place, there's no need for a runtime to dynamically adjust the layout and size of a type, and unsafe code can exactly know and rely on the size of a type being constant.
If traits were allowed to define structs, all or most of this would break down. So, view it as a technical necessity.
and traits with fields are nothing, but classes, that enable multiple inheritance by being classes with some restrictions.
The primary question you should ask yourself is, "what does my program do?". Data structures and functions are simply a tool to help you organize your program, but none are required for your program to work. What I recommend is, instead of focusing on the way to the goal, you should focus on the goal itself and let abstractions arise naturally on your way to achieve the goal. Abstractions are only useful, if they serve a purpose, otherwise they add unnecessary complexity to your program.
Thank @H2CO3 and @Hyeonu for kindly reply.
I believe that Rust team must have sufficient reason to do so. But I still want to know how to implement the following feature in more graceful way.
the example refer to @Hyeonu 's reply
struct BaseMaterial {
sentense: String,
word: String,
//more fields need accessed in trait 'Say'
...
}
trait Say {
fn say(&self) {
self.translate(self.get_sentense());
self.translate(self.get_word());
}
//getter method
fn get_sentense(&self)-> &str;
fn get_word(&self)-> &str;
// more getter method
...
// virtual function need be implement by sub-type
fn translate<'a> (&self, original: &'a str)-> &'a str;
}
struct SayEnglish {
material: BaseMaterial,
...
}
impl Say for SayEnglish {
fn get_sentense(&self)-> &str {
&self.material.sentense
}
fn get_word(&self)-> &str {
&self.material.word
}
//more bridge method
...
// implement virtual function
fn translate<'a> (&self, original: &'a str)-> &'a str {
//translate
original
}
}
thanks for kindly reply.
My question about "why not rust allow fields in trait" is not challenge why rust not implement like virtual function, but try to understanding the cause why they do like this