📖 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.