Here's a (somewhat contrived) example to illustrate what I would like to do
pub trait Node: Eq + Hash {
type Edge: Edge;
fn get_in_edges(&self) -> Vec<&Self::Edge>;
fn get_out_edges(&self) -> Vec<&Self::Edge>;
}
pub trait Edge {
type Node: Node;
fn get_src(&self) -> &Self::Node;
fn get_dst(&self) -> &Self::Node;
}
pub trait Graph {
type Node: Node;
type Edge: Edge;
fn get_nodes(&self) -> Vec<Self::Node>;
}
pub fn dfs<G: Graph>(root: &G::Node) {
let mut stack = VecDeque::new();
let mut visited = HashSet::new();
stack.push_front(root);
while let Some(n) = stack.pop_front() {
if visited.contains(n) {
continue
}
visited.insert(n);
for e in n.get_out_edges() {
stack.push_front(e.get_dst());
}
}
}
Is there a way to express in the Graph
trait that Graph::Node
must be the same type as Graph::Edge::Node
and that Graph::Edge
must be the same type as Graph::Node::Edge
?
I remember reading something about a feature (not implemented at the time) that would allow richer constraints for this sort of thing, but I don't remember its name and cannot find it.
What is an associated type? An associated type can be seen as a replacement of a specific type within a protocol definition. In other words: it's a placeholder name of a type to use until the protocol is adopted and the exact type is specified.
GATs (generic associated types) were originally proposed in RFC 1598. As said before, they allow you to define type, lifetime, or const generics on associated types. If you're familiar with languages that have "higher-kinded types", then you could call GATs type constructors on traits.
A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.
A trait in Rust is a group of methods that are defined for a particular type. Traits are an abstract definition of shared behavior amongst different types. So, in a way, traits are to Rust what interfaces are to Java or abstract classes are to C++. A trait method is able to access other methods within that trait.
In Graph
's definition, you can constrain each associated type's associated type (!) to be equal to the corresponding associated type in Graph
.
pub trait Graph {
type Node: Node<Edge = Self::Edge>;
type Edge: Edge<Node = Self::Node>;
fn get_nodes(&self) -> Vec<Self::Node>;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With