TO parameterize the types of function, we need to name the type parameter. We have to declare the parameter name in the signature so that the compiler knows what that name means. Similarly, when we use a type parameter name in a function signature, we have to declare the type param before we use it.
fn largest<T>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
We can also define structs to use a generic type parameter in one or more fields using the <>
syntax.
struct Point<T> {
x: T,
y: T,
}
fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
}
We can define enums to hold generic data types as well. One example is the Option
enum discussed earlier.
enum Option<T> {
Some(T),
None,
}
// Multiple generic types are supported as well
enum Result<T,E> {
Ok(T),
Err(E),
}
When implementing methods on a type that has generic type, we need to specify the <T>
in front of the method so Rust can identify that the type in the angle brackets in that type is a generic type.
struct Example <T> {...}
impl<T> Example<T> {
...
}
impl Example<i32> {
// methods here will only be available when Example is of
// the i32 type
}
A trait defines functionality a particular type has and can share with other types. We can use traits to define shared behavior in an abstract way. We can also use trait bounds to specify that a generic type can be any type that has certain behavior. It is similar to interface in other languages but different.
A type’s behavior consists of the methods we can call on that type. Trait definitions are a way to group method signatures together to define a set of behaviors necessary to accomplish some purpose.
Here’s an example. Suppose we have different kinds of articles like NewsArticle
, Tweets
, etc. We are interested in building an aggregator, and we’d like to have each of the articles have a Summary
trait so we can call summarize
on each of them for our aggregator.
pub trait Summary {
fn summarize(&self) -> String;
fn view_more(&self) -> String {
String::from("(Read more..)")
}
}
Here, we only declare the signature because it’s up for the struct itself to implement the method. However, we could specify a default behavior if the struct using this trait does not implement such method.