There are some methods that every struct implementing my trait need to implement.
There is a different set of methods that every struct implementing my trait need to use.
The following example is for demonstration purposes
pub trait Migration {
type Location: std::fmt::Display;
/// should be defined and usable
/// is used in other contexts
fn validate_destination(&self, destination: &Self::Location) -> bool;
/// should be defined, but not re-usable
/// could result in undefined behavior if not used with a valid destination
/// please use self.migrate instead
fn migrate_to_destination(&mut self, destination: &Self::Location);
/// should be usable, but not re-definable
fn migrate(&mut self, destination: &Self::Location) -> Result<String, String> {
if self.validate_destination(destination) {
self.migrate_to_destination(destination);
Ok(format!("Migrated freely to {destination}"))
} else {
Ok("Invalid destination".to_string())
}
}
}
Column 1
Column 2
Column 3
Column 4
should be implemented by trait implementor
should be used by trait importer
validate_destination
yes
yes
migrate_to_destination
yes
NO
migrate
NO
yes
I tried creating and importing a subtrait, but it didn't help because knowing the associated types forced the trait implementor to import the supertrait.
For other direction I do not know of any good way, but in general if you can define some function, nothing prevents you from calling it, or at least executing the same piece of code, so there might be no clean way to achieve that.
if the implementors are all local types, and consumers are external, you can "seal" the trait or some of the methods so they are not callable from downstream crates.
you can combine the blanket impl trick by @Tom47 and the techniques from this blog post about sealed traits: