sudo dnf install rust rust-doc.x86_64 rust-std-static.x86_64 rust-debugger-common.noarch rust-analysis.x86_64 rust-packaging.x86_64 --best --allowerasing -y
call plug#begin('~/.vim/plugged') " Rust Plug 'rust-lang/rust' Plug 'rust-lang/rust.vim' call plug#end()
let var = "lol"; var = "chorizo";
let mut var = 1; var = var + 1; var = var + 15;
fn main() { println!("Hello World"); }
fn main() { let a = 1; let b = 2; println!("{} + {} is {}", a, b, a+b); }
// Bools let x = true; let x: bool = true; let y = false && true; #False // Chars // Not 8-bit like C, but unicode scalars which means that support unicode ootb let c: char = "A"; let d: char = ""; // Integers (like C) let a: i8 = -15; let b: u8 = 250; let c: i16 = -356; let d: u16 = 1029; let f: i32 = -31337; let g: u32 = 42949672; let h: i64 = -4294967295; let i: u64 = 18446744073709551615; let j: isize = -1; # Signed let k: usize = 1; # Unsigned // Floating let single: f32 = 1.1225 * 10.0; let double: f64 = 1.2e-15; //Containers let array: [i8, 3] = [1, 4, 7]; let tuple: (i32, char) = (21, 'x'); println!("{}, {}", array[1], tuple.1); # Will print 4, x // Functions Parameters fn add(a: i32, b: i32) -> i32 { return a + b; } fn add_2(a: i32, b: i64) -> i32 { return a + (b as i32); } fn main() { let a = 1; let b = 3; println!("{} + {} is {}", a, b, add(a,b)); println!("{} + {} is {}", a, b, add_2(a,b)); }
When you concatenate strings, you need to allocate memory to store the result. The easiest to start with is String and &str:
fn main() { let mut owned_string: String = "hello ".to_owned(); let borrowed_string: &str = "world"; owned_string.push_str(borrowed_string); println!("{}", owned_string); }
Here, we have an owned string that we can mutate. This is efficient as it potentially allows us to reuse the memory allocation. There's a similar case for String and String, as &String can be dereferenced as &str.
fn main() { let mut owned_string: String = "hello ".to_owned(); let another_owned_string: String = "world".to_owned(); owned_string.push_str(&another_owned_string); println!("{}", owned_string); }
After this, another_owned_string is untouched (note no mut qualifier). There's another variant that consumes the String but doesn't require it to be mutable. This is an implementation of the Add trait that takes a String as the left-hand side and a &str as the right-hand side:
fn main() { let owned_string: String = "hello ".to_owned(); let borrowed_string: &str = "world"; let new_owned_string = owned_string + borrowed_string; println!("{}", new_owned_string); }
Note that owned_string is no longer accessible after the call to +.
What if we wanted to produce a new string, leaving both untouched? The simplest way is to use format!:
fn main() { let borrowed_string: &str = "hello "; let another_borrowed_string: &str = "world"; let together = format!("{}{}", borrowed_string, another_borrowed_string); println!("{}", together); }
Note that both input variables are immutable, so we know that they aren't touched. If we wanted to do the same thing for any combination of String, we can use the fact that String also can be formatted:
fn main() { let owned_string: String = "hello ".to_owned(); let another_owned_string: String = "world".to_owned(); let together = format!("{}{}", owned_string, another_owned_string); println!("{}", together); }
You don't have to use format! though. You can clone one string and append the other string to the new string:
fn main() { let owned_string: String = "hello ".to_owned(); let borrowed_string: &str = "world"; let together = owned_string.clone() + borrowed_string; println!("{}", together); }
Note - all of the type specification I did is redundant - the compiler can infer all the types in play here. I added them simply to be clear to people new to Rust, as I expect this question to be popular with that group!
use std::collections::LinkedList; fn main() { let mut ll = LinkedList::new(); ll.push_back(1); ll.push_back(2); ll.push_back(4); for a in ll { println!("{}", a); } }
use std::collections::Vec; fn main() { let mut v = Vec::new(); v.push('x'); v.push('y'); v.push('z'); for a in v { println!("{}", a); } }
use hello::say_hello; mod hello { pub fn say_hello() { println("Hello World!"); } } fn main() { say_hello(); }
Eliminates all mem unsafety then will never seg fault
With a strict static compile time checking (Borrow checker:
GC: Statically compiled, minimum impact on performance
Concurrency:
Ownership sample:
// a owns the value let a = foo(); // now b owns the value let b = a; // ownership passed to do_something() do_something(b); // This will fail do_something_else(b);
How Ownership works:
Borrowing sample
// a owns the value let a = foo(); // a still owns the value let b = &a; // But functions still could use it do_something_yet_again(&a);
How Mut and non-Mut Borrowing works:
Mutable Borrowing sample
let a = foo(); let b = &a; // Error, Cannot move borrowed value do_something(a);
Mutable borrows:
Sample Mut and non-Mut to the same borrow
let mut a = 10; let b = &mut a; b += 1; // Error cannot have mutable and inmutable borrows let c = &a; b += 1;
RAII
RAII Sample
fn munge_file() { let mut file = File::create("file.txt")?; file.write_all(b"hello world"); } // file closed auto when the functions finishes because no-one is owning the file handler
fn take_ownership_of_value(v: Vec<i32>) -> i32 { let mut sum = 0; for val in v { sum += val; } return sum; } fn main() { let arr = vec![1,2,3,4,5,6,7,8,9]; let sum = take_ownership_of_value(arr); // This will give us an err because the function 'take_ownership_of_value' // owns the value and when finishes frees the resource. println!("Sum of {} values: {}", arr.len(), sum); }
fn borrow_sum (v: &Vec<i32>) -> i32 { // Receives a reference (mem position) let mut sum = 0; for val in v { // We iterate over mem positions. // This is a pointer that points to the content. sum += *val; } return sum; } fn main() { let arr = vec![1,2,3,4,5,6,7,8,9]; let sum = borrow_sum(&arr); println!("Sum of {} values: {}", &arr.len(), sum); }
fn cap_value_borrow(max: i32, v: &mut Vec<i32>) { for index in 0..v.len() { if v[index] > max { v[index] = max; } } } fn main() { let mut arr = vec![1,2,3,500000,4,5]; cap_value_borrow(10, &mut arr); for v in arr { println!("{}", v); } } // The output :!./vec_mut_ref 1 2 3 10 4 5
Slices & Strs
Sample code
let a = vec![1,2,4,6,7,8]; let sla = &a[1..2]; // sla = &[2,4] let b = String::from("Hello"); let slb = &b[1..2]; // slb = "el"
#[derive(Debug, Deserialize)] pub struct Global { pub repository_path: String, pub content_root_path: String, } #[derive(Debug, Deserialize)] pub struct Settings { pub debug: bool, pub global: Global, } let config = matches.value_of("config").unwrap(); let settings = Settings::new(config).unwrap() println!("You have all of this topics at {}/{}:\n", settings.global.repository_path, settings.global.content_root_path);
#[derive(PartialEq)] enum Animal { Cat, Dog, } // This will fail because are not the same animal. fn main() { let pet = Animal::Dog; let other_pet = Animal::Cat; assert!(pet == other_pet); }
enum Action { Drive, Turn(Direction), Stop } enum Direction { Left, Right, } print_action(a: Action) { match a { // The compiler will take care about to cover all cases Action::Drive => println!("Go Forward"), Action::Turn(direction) => match direction { // This is chcking inside of Direction enum Direction::Left => println!("Turn Left!!"), Direction::Right => println!("Turn Right!!"), } Action::Pickup => println!("Pick up Object!"), Action::Stop => println!("Stop!") } } fn main() { let program = vec![ Action::Drive, Action::Turn(Direction::Left), Action::Drive, Action::Pickup, Action::Turn(Direction::Left), Action::Turn(Direction::Left), Action::Turn(Direction::Left), Action::Drive, Action::Turn(Direction::Right), Action::Drive, Action::Stop ]; for action in program { print_action(action) } }
#[derive(Copy, Clone)]
enum MachineState {
Normal,
Comment,
Upper,
Lower,
}
fn machine_cycle(state: MachineState, c: char) -> (Option<char>, MachineState) {
use self::MachineState::*;
match (state, c) {
(Normal, '#') => (None, Comment),
(Normal, '^') => (None, Upper),
(Normal, '_') => (None, Lower),
(Normal, other) => (Some(other), Normal),
(Comment, '#') => (None, Normal),
(Comment, _) => (None, Comment),
(Upper, '^') => (None, Normal),
(Upper, other) => (Some(other.to_ascii_uppercase()), Upper),
(Lower, '_') => (None, Normal),
(Lower, other) => (Some(other.to_ascii_lowercase()), Lower),
}
}
fn main(){
let mut state = MachineState::Normal;
let mut processed_string = String::new();
let input = "This _Is_ some ^input^. #we want this trnasformed without this comment#";
for character in input.chars() {
let (output, new_state) = machine_cycle(state, character);
if let Some(c) = output {
processed_string.push(c);
}
state = new_state;
}
println!("{}", processed_string);
}
"This _Is_ some ^input^. #we want this trnasformed without this comment#"
This is some INPUT.
Traits:
Non-so much useful sample xD
struct Foo {x: u32} trait Print { fn print(&self); } impl Print for Foo { fn print(&self) { println!("{}", self.x) } } // To call this implementation just use this snippet // let a = Foo {x = 20}; // a.print()
struct config { has_config: bool, file_name: String, path: String, } trait Create { fn create(&self); } impl Create for config { fn create(&self) { if (self.has_config) { File::create(format!("{}/{}", self.path, self.file_name)); } } }
struct Dwarf { name: String } struct Elf { name: String } struct HalfOrc { name: String } struct Human { name: String } pub trait Constitution { fn constitution_bonus(&self) -> u8 { 0 } } impl Constitution for HalfOrc { fn constitution_bonus(&self) -> u8 { 1 } } impl Constitution for Dwarf { fn constitution_bonus(&self) -> u8 { 2 } } fn main() { let my_dwarf = Dwarf {name: String::from("ParriDwarf")}; let my_horc = HalfOrc {name: String::from("ParriHOrc")}; println!("{}", my_dwarf.constitution_bonus()); println!("{}",my_horc.constitution_bonus()); }
Built-in traits
Non-partial means infallible
Eq/PartialEq: Allow values to be compared and put in order
Ord/PartialOrd: Ordering elements of the same type
if a < b && b < c { a < c } if a == b && b == c { a == c }
println!("{}", display);
println!("{:?}", debug); // non-pretty view println!("{:#?}", debug); // pretty view
let a = foo(); let b = a.clone();
for ... in ...
syntax to work on HashMap, Vec, LinkedList, ... and many others collectionsGeneric functions
fn print<T: Display>(t: T) { println!("{}", t); }
Display
traitTrait
and the function associatedGeneric Types
struct Tagged<T> { value: T, tag: String. } impl<T> Tagged<T> { fn tag(&self) -> String { self.tag.clone() } }
Static Distpatching
Dynamic Distpatching
fn show_all(v: Vec<&dyn Display>) { for item in v { println!("{}", item); } } fn main() { // Here we force the reference to 12 to be a Display trait type, also with the String // and then we passes them to show_all function which receives a Dynamic Vector that contains // just Display types, and then get them printed. let v = vec![&12 as &Display, &"Hi all!" as &Display]; // This is quite important because in the end we've changed the morpholoy show_all(v); }
- Monomorphization is preferred, and you need to be sure that you want to use this feature - Ugly sintax and also long compile times - It uses v-tables, introduction to extra pointer lookup
- Which means: We have a Pointer to the argumment (cannot be passed by value), then you have a pointer to the v-table and this v-table will have a pointer to a function which is actually called. This have a very serious performance penalty like in an example Dyn Distpatch it's called in a loop in a performance critical section of the code
Closures:
fn main() { let closure1 = |x| { x + 1 }; println!("{}", closure1(2)); //prints 3 }
| arg1, arg2, argN |
fn main() { let val = 10; let closure2 = |x| { x + val }; println!("{}", closure2(2)); // Prints 12 }
fn main() { let c: Type = |x| { x + 1 }; }
fn <T: Fn(i32) -> i32> f1()-> T { let f = |x| { x + 1 }; return f; } fn f2() -> Box<dyn Fn(i32) -> i32 > { let f = |x| { x + 1}; return f; }
Threads
use std::thread let handle = thread::spawn(|| { println!("From a thread!"); }); println!("Before a thread!") // Wait for execution handle.join();
Iterators
x.into_iter()
: Gives an iterator over Tx.iter_mut()
: Gives an iterator over &mut Tx.iter()
: Gives an iterator over &Tlet mut iterator = (1..5).into_iter(); iterator.next(); //Some(1) iterator.next(); //Some(2) iterator.next(); //Some(3) iterator.next(); //Some(4) iterator.next(); //None iterator.next(); //None iterator.next(); //None
when the iterator gets exhausted, returns None
Other methods:
take()
& skip()
Sample code:let mut iterator(1..10).into?iter(); iterator.skip(2); iterator.next(); //Some(3) let taken = iterator.take(2); taken.next(); //Some(4) taken.next(); //Some(5) taken.next(); //None
enumerate()
:
let mut iterator = vec![ "A", "B", "C"].into_iter(); let enumerated = iterator.enumerate(); enumerated.next(); //Some((0, "A")) enumerated.next(); //Some((1, "B"))
Iterators are Lazy!, this is important because just store in memory the actual value and the next one, the you could use it on a interator without exhaust the memory
You could also concile the iterator using the trait collect()
let mut iterator = (1..10).into_iter(); iterator.skip(2); let taken = iterator.take(4); lev v: Vec<i32> = taken.collect(); // vec![3,4,5,6]
Map, Filer and Fold
let items = (1..10).into_iter(); let other_items: Vec<i32> = items.map(|x| { x + 1 }).collect();
this ^^ is like:
let items = (1..10).into_iter(); let mut other_items = Vec::new(); for item in items { other_items.push(x + 1); }
let items = (1..10).into_iter(); let other_items: Vec<i32> = iter .map(|x| { foo(x, 12) }) //i32 .map(|x| { bar(x, "Hello") }) //String .map(|x| { baz(x) }) //String .map(|x| { fuz(x, bar(x)) }) //i32 .collect();
let items = (1..10).into_iter(); let evens: Vec<_> = items .filter(|x| { x % 2 == 0 }). .collect(); // vec![2,4,6,8]
reduce
in other langslet items = (1..10).into_iter(); items.fold(0, |sum, item| { sum + item }); //45
(1..100).into_iter(); .filter(|x| {2 % x == 1}) .map(|x| {x * x}) .filter(|x| {x % 5 != 0}) .fold(0, |sum, x| {sum + x});