Using generics

Now, the way we instantiate or use generic types is also a bit different than their non-generic counterparts. Any time we instantiate them, the compiler needs to know the concrete type in place of T in their type, signature, which gives it the type information to monomorphize the generic code. Most of the time, the concrete type is inferred based on the instantiation of the type or by calling any method that takes a concrete type in the case of generic functions. In rare cases, we need to help the compiler by specifically typing out the concrete type in place of the generic type by using the turbofish (::<>) operator. We'll see how that is used in a moment.

Let's look at the case of instantiating Vec<T>, a generic type. Without any type signature, the following code does not compile:

// creating_generic_vec.rs

fn main() {
let a = Vec::new();
}

Compiling the preceding code, gives the following error:

This is because the compiler doesn't know what type a would contain until we specify it manually or call one of its methods, thereby passing in a concrete value. This is shown in the following snippet:

// using_generic_vec.rs

fn main() {
// providing a type
let v1: Vec<u8> = Vec::new();

// or calling method
let mut v2 = Vec::new();
v2.push(2); // v2 is now Vec<i32>

// or using turbofish
let v3 = Vec::<u8>::new(); // not so readable
}

In the second code snippet, we specified the type of v1 to be a Vec of u8, and it compiles fine. Another way, as with v2, is to call a method that accepts any concrete type. After the push method call, the compiler can infer that v2 is a Vec<i32>. The other way to create the Vec is to use the turbofish operator, as is the case with v3 binding in the preceding code.

The turbofish operator in generic functions appears right after the function name and before the parenthesis. Another example of this is the generic parse function from the std::str module. parse can parse values from a string, and many types are able to parse from it, such as i32, f64, usize, and so on, so it's a generic type. So, when using parse, you really need to use the turbofish operator, like so:

// using_generic_func.rs

use std::str;

fn main() {
let num_from_str = str::parse::<u8>("34").unwrap();
println!("Parsed number {}", num_from_str);
}

Something to take note of is that only types that implement the FromStr interface or trait can be passed to the parse function. u8 has an implementation of FromStr, and so we were able to parse it in the preceding code. The parse function uses the FromStr trait to limit types that can be passed to it. We'll get to know how we can mix generics and traits after we're done exploring traits.

With the idea of generics under our belt, let's focus on one of the most ubiquitous features in Rust, traits!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset