π Sometimes, when writing a function or data type, we may want it to work for multiple types of arguments. In Rust, we can do this with generics.
π The concept is, instead of declaring a specific data type we use an uppercase letter(or CamelCase identifier). ex, instead x : u8 we use x : T . but we have to inform to the compiler that T is a generic type(can be any type) by adding <T> at first.
Generalizing functions
fntakes_anything<T>(x:T) { // x has type T, T is a generic type}fntakes_two_of_the_same_things<T>(x:T, y:T) { // both x and y has same type}fntakes_two_things<T, U>(x:T, y:U) { // multiple types}
Generalizing structs
structPoint<T> { x:T, y:T,}fnmain() {let point_a =Point { x:0, y:0 }; // T is a int typelet point_b =Point { x:0.0, y:0.0 }; // T is a float type}// π When addding an implementation for a generic struct, the type parameters should be declared after the impl as well
// impl<T> Point<T> {
βοΈ Above Option and Result types are kind of special generic types which are already defined in Rustβs standard library.
An optional value can have either Some value or no value/ None.
A result can represent either success/ Ok or failure/ Err
Usages of Option
// 01 - - - - - - - - - - - - - - - - - - - - - -fngetIdByUsername(username:&str) ->Option<usize> {//if username can be found in the system, set userIdreturnSome(userId);//elseNone}//π So on above function, instead of setting return type as usize// set return type as Option<usize>//Instead of return userId, return Some(userId)// else None (π‘remember? last return statement no need return keyword and ending ;)// 02 - - - - - - - - - - - - - - - - - - - - - -structTask {String, assignee:Option<Person>,}//π Instead of assignee: Person, we use Option<Person>//Because the task has not been assigned to a specific person// - - - - - - - - - - - - - - - - - - - - - - -//when using Option types as return types on functions// we can use pattern matching to catch the relevant return type(Some/None) when calling themfnmain() {let username ="anonymous"matchgetIdByUsername(username) {None=>println!("User not found"),Some(i) =>println!("User Id: {}", i) }}
Usages of Result
π The Option type is a way to use Rustβs type system to express the possibility of absence. Result expresses the possibility of error.
// - - - - - - - - - - - - - - - - - - - - - -fnget_word_count_from_file(file_name:&str) ->Result<u32, &str> {//if the file is not found on the system, return errorreturnErr("File can not be found!")//else, count and return the word count//let mut word_count: u32; ....Ok(word_count)}//π on above function,// instead panic(break) the app, when a file can not be found; return Err(something)// or when it could get the relevant data; return Ok(data)// - - - - - - - - - - - - - - - - - - - - - - -// we can use pattern matching to catch the relevant return type(Ok/Err) when calling itfnmain() {letmut file_name ="file_a";matchget_word_count_from_file(file_name) {Ok(i) =>println!("Word Count: {}", i),Err(e) =>println!("Error: {}", e) }}
π Many useful methods have been implemented around Option and Result types. More information can be found on std::option::Option and std::result::Result pages on Rust doc.
βοΈ Also more practical examples of options & results can be found on Error Handling section in Rust doc.