Sometimes, we need our type to have generic type parameters, but we don't want them to be totally generic. For example, we might need the type that's substituted for the parameter to be able to be moved between threads, or to support transformation into String, or any number of other things. Fortunately, Rust provides a way for us to do that.
We limit the domain of a generic type parameter by requiring it to have one or more traits. This is called a trait bound. Let's look at a basic binary tree data structure as an example.
Here are a pair of structures that represent a binary tree, with generic type parameters for the key and value types, and a trait bound on the key type to make sure it actually supports the comparisons we need for a binary tree key:
struct TreeNode<K, V> where K: PartialOrd + PartialEq {
key: K,
value: V,
lesser: Option<Box<TreeNode<K, V>>>,
greater: Option<Box<TreeNode<K, V>>>,
}
Here is the second structure, which gives us a way to store an empty tree:
pub struct Tree<K, V> where K: PartialOrd + PartialEq {
root: Option<Box<TreeNode<K, V>>>,
}
We need the second structure so that a tree containing no data can be represented. On both of these structures, we've placed the names of the generic type parameters between < and > after the structure name, but then we included a where clause that says that K: PartialOrd + PartialEq. That means that any data type that is substituted for K must implement both the PartialOrd trait and the PartialEq trait. If we try to use a data type that does not implement both traits, the compiler will reject it.
We've also specified that lesser and greater in TreeNode and root in Tree are variables with the Option<Box<TreeNode<K, V>>> data type . That means that they are optional (they can contain a meaningful value, or None), and if they contain a meaningful value, it is stored on the heap, and that value stored on the heap is a TreeNode with K as the data type of its key, and V as the data type of its value.