Rust 101 (Part 1)
- Published on
The demand for Rust developers is increasing, leading to a highly competitive salary race.
The reasons why tech giants and developers select Rust.
- Super rapid (faster than Python, Go)
- Modern style grammer
- From OS to web development
- So many tools called crates
- Strongly safe
- Version compatible
Microsoft is rewriting C++ based source codes into Rust.
Super Rapid
Rust is compiled to machine language directly, doesn't have GC, and pursues zero cost abstruction. These are the main reasons why Rust is brazing fast. 🚀🚀🚀🚀
And Rust selected applying "Ownership," "Borrowing," and "Lifetime."
Modern style grammer
If you are familiar with some programming languages, such as Python or JavaScript, you may not care too much about how the variable is handled.
However, if you need to write Rust, you must care about it. The below codes have compile errors.
Before
fn main() {
let x = 10;
println!("The number is: {}", x); // The number is: 10
x = 20;
println!("The number is: {}", x); // Compile error
}
After
fn main() {
let mut x = 10;
println!("The number is: {}", x); // The number is: 10
x = 20;
println!("The number is: {}", x); // The number is: 20
}
If you want to change x valuable, you need to put mut word before valuable name.
Iterator
The below function filtered all the even numbers from source
vector by using filter, map, and closure.
fn main() {
let source = vec![1, 2, 3, 4, 5];
let result = source
.into_iter()
.filter(|n| n % 2 == 0)
.map(|n| n.to_string())
.collect::<Vec<String>>();
println!("{:?}", result);
//output: ["2", "4"]
}
Instead of for
, filter
and map
enable us to read from top to bottom to read the code naturally.
Pattern matching
The Pattern matching is super powerful. Rust is not allowed to be null, so you need to implement Option
like the below.
fn main() {
let num: Option<i32> = Some(2);
match num {
Some(x) if x % 2 == 0 => println!("Even number: {}", x),
Some(x) => println!("Odd number: {}", x),
None => println!("No value"),
}
// output: Even number: 2
}
pub enum Option<T> {
// NO VALUE
None,
// Some value "T"
Some(T)
}
Type Inference
Rust has strong support for type inference, which is coming from other functional programming languages such as Haskell.
let mut v = vec![]; // v doesn't know about own type yet.
v.push(1); // Ok, my type is i32😎
Trait
Trait is similar to Java and Golang's interface.
fn main() {
let dog = Dog{};
let cat = Cat{};
show_animal_data(dog);
show_animal_data(cat);
}
trait Animal {
fn age(&self) -> u32;
fn name(&self) -> String;
}
struct Dog;
impl Animal for Dog {
fn age(&self) -> u32 {
9
}
fn name(&self) -> String {
"Taro".to_string()
}
}
struct Cat;
impl Animal for Cat {
fn age(&self) -> u32 {
12
}
fn name(&self) -> String {
"Pochi".to_string()
}
}
fn show_animal_data<T: Animal>(animal: T) {
println!("Age: {} years", animal.age());
println!("Name: {}", animal.name());
}
The cat
and dog
have Animal
trait, and show_animal_data
don't care about exact type. Without Animal
trait, show_animal_data
is needed to be implemented twice like the below.
// Without trait
fn show_dog_data(dog: Dog) {
println!("Age: {} years", dog.age());
println!("Name: {}", dog.name());
}
fn show_cat_data(cat: Cat) {
println!("Age: {} years", cat.age());
println!("Name: {}", cat.name());
}
// fn show_fox_data(fox: Fox) {
// println!("Age: {} years", fox.age());
// println!("Name: {}", fox.name());
// }
// fn show_penguin_data(penguin: Penguin) {
// println!("Age: {} years", penguin.age());
// println!("Name: {}", penguin.name());
// }
That is why you must create similar functions whenever you implement an animal, such as a Fox or penguin. But if satisfying the Animal
trait, you implement the create function once.
Thanks for reading this article!