Let's say I have common logic that depends intimately on data members as well as a piece of abstract logic. How can I write this in rust types without rewriting the same code for each implementation?
Here's a toy example of what I might write in scala. Note that the abstract class has concrete logic that depends on both the data member name
and abstract logic formatDate()
.
abstract class Greeting(name: String) {
def greet(): Unit = {
println(s"Hello $name\nToday is ${formatDate()}.")
}
def formatDate(): String
}
class UsaGreeting(name: String) extends Greeting {
override def formatDate(): String = {
// somehow get year, month, day
s"$month/$day/$year"
}
}
class UkGreeting(name: String) extends Greeting {
override def formatDate(): String = {
// somehow get year, month, day
s"$day/$month/$year"
}
}
This is just a toy example, but my real life constraints are:
name
).struct
s continue to hold all those data members and complex methods.Here are some somewhat unsatisfactory ideas I had that could make this work in rust:
get_name()
method on the trait that every implementation would need. But this seems unnecessarily verbose and might also cause a performance hit if the getter doesn't get inlined.I'm not fully happy with these ideas, so is there a better way in rust to mix abstract logic with concrete logic that depends on data members?
The most general solution seems to be my original 3rd bullet: instead of a trait, make a struct with a generic whose associated functions complete the functionality.
For the original toy Greeting
example Denys's answer is probably best. But a more general solution that addresses main question is:
trait Locale {
pub fn format_date() -> String;
}
pub struct Greeting<LOCALE: Locale> {
name: String,
locale: PhantomData<LOCALE>, // needed to satisfy compiler
}
impl<LOCALE: Locale> Greeting<LOCALE> {
pub fn new(name: String) {
Self {
name,
locale: PhantomData,
}
}
pub fn greet() {
format!("Hello {}\nToday is {}", self.name, LOCALE::format_date());
}
}
pub struct UsaLocale;
impl Locale for UsaLocale {
pub fn format_date() -> {
// somehow get year, month, day
format!("{}/{}/{}", month, day, year)
};
}
pub type UsaGreeting = Greeting<UsaLocale>;
pub type UkGreeting = ...