Rust: Difference between revisions

From bibbleWiki
Jump to navigation Jump to search
 
(370 intermediate revisions by the same user not shown)
Line 1: Line 1:
= Terms =
**Fat Pointer Contains Address actual data and length
= Sample program =
<syntaxhighlight lang="rust">
fn main() {
    println!("Hello, world!");
}
</syntaxhighlight>
= Cargo =
= Cargo =


Line 27: Line 17:
</syntaxhighlight>
</syntaxhighlight>


= Types and Variables=
=Fundamental Data Types=
==Primitive types==
Cam declare with size of type or without
<syntaxhighlight lang="rust">
// Integers


== Fundamental Data Types ==
=== Primitive types ===
Cam declare with size of type
<syntaxhighlight lang="rust">
  let a:u8 = 123; // unsigned int 8 bits number immutable
  let a:u8 = 123; // unsigned int 8 bits number immutable
  let a:i8 = 123; // signed int 8 bits number immutable
  let a:i8 = 123; // signed int 8 bits number immutable
  let mut a:u8 = 123; // unsigned int 8 bits number mutable
  let mut a:u8 = 123; // unsigned int 8 bits number mutable
</syntaxhighlight>
 
Or without e.g.
<syntaxhighlight lang="rust">
  let mut c = 123456789 // 32-bit signed i32
  let mut c = 123456789 // 32-bit signed i32
  println!("c = {}", c);
  println!("c = {}", c);
</syntaxhighlight>
 
Now variable based on OS e.g.
// Based on OS e.g.
<syntaxhighlight lang="rust">
  let z:isize = 123 // signed 64 bit if on 64 bit OS
  let z:isize = 123 // signed 64 bit if on 64 bit OS
</syntaxhighlight>


=== Decimal ===
// Decimal
  let e:f64 = 2.5 // double-precision, 8 bytes or 64-bits
  let e:f64 = 2.5 // double-precision, 8 bytes or 64-bits
=== Char ===
 
// Char
  let x:char = 'x' // Note 4 bytes unicode
  let x:char = 'x' // Note 4 bytes unicode
=== boolean ===
 
// Boolean
  let g:bool = false; // Note 4 bytes unicode
  let g:bool = false; // Note 4 bytes unicode


== Operators ==
</syntaxhighlight>
Does not support -- and ++ but does support
==Operators==
<syntaxhighlight lang="rust">
// Does not support -- and ++ but does support
  a -= 2;
  a -= 2;
Remainder can be calculated using
 
// Remainder can be calculated using
  a%3
  a%3
Bitwise
 
// Bitwise
  let c = 1 | 2 // | OR
  let c = 1 | 2 // | OR
Shift
 
// Shift  
  let two_to_10 = 1 << 10; // 1024
  let two_to_10 = 1 << 10; // 1024
Logical of standard e.g.
 
// Logical of standard e.g.
  let pi_less_4 = std::f64::consts::PI < 4.0; // true
  let pi_less_4 = std::f64::consts::PI < 4.0; // true


== Scope and shadowing ==
</syntaxhighlight>
==Scope and shadowing==
Curly braces keep scope
Curly braces keep scope
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
Line 88: Line 83:
   println!("5 {a}");
   println!("5 {a}");
  }
  }
</syntaxhighlight>
==Constants==
<syntaxhighlight lang="rust">
// Standard const
const MEANING_OF_LIFE:u8 = 42;
// Static const
static Z:i32 = 123;
</syntaxhighlight>
</syntaxhighlight>


== Constants ==
=Types=
Standard const
==Tuples==
Eezy peezy lemon squeezy
<syntaxhighlight lang="rust">
fn sum_and_product(x:i32,y:i32) -> (i32, i32)
{
(x+y, x*y)
}
 
fn main()
{
  let sp = sum_and_product(3,4);
  let (a,b) = sp;
  let sp2 = sum_and_product(4,5);
 
  // combine
  let combined = (sp, sp2);
  let ((c,d), (e,f)) = combined;
}
</syntaxhighlight>
==Arrays==
Array sizes cannot grow in rust
===Simple===
<syntaxhighlight lang="rust">
let mut a:[i32;5] = [1,2,3,4,5];
// Or
let mut a = [1,2,3,4,5];
// Length
a.len()
// Assignment
a[0] = 321
// Printing
println!("{:?}", )
// Testing
  if a == [1,2,3,4,5]
  {
  }
// Initialise
  let b = [1,10]; // 10 array initialised to 1
 
</syntaxhighlight>
=== Multi Dimension ===
Here is a two dimension array
<syntaxhighlight lang="rust">
let mtx:[[f32;3];2] =
[
  [1.0, 0.0, 0.0],
  [0.0, 2.0, 0.0],
];
 
</syntaxhighlight>
 
==Slices==
A slice is a non-owning pointer to a block of memory. For example
<syntaxhighlight lang="rust">
 
// Create a vector
let v: Vec<i32> = {0..5}.collect();
 
// Now create a slice (reference)
let sv: &[i32]= &v;
 
// We create a slice with only some elements
let sv1: &[i32]= &v[2..4];
 
// Printing these will produce the same result
println!("{:?}",v);
println!("{:?}",sv);
 
// And the range
println!("{:?}",sv1);
 
</syntaxhighlight>
 
==Strings==
Basic String
<syntaxhighlight lang="rust">
let name = String::from("Iain");
let name = "Iain".to_string();
</syntaxhighlight>
 
Two types, static string and string type
<syntaxhighlight lang="rust">
let s = "hello";
// Cannot do
// let h = s[0]
// You can iterate as a sequence using chars e.g.
for c in s.chars()
{
  println!("{}", c);
}
</syntaxhighlight>
And now the mutable string in rust essentially an vector
// Create a string
<syntaxhighlight lang="rust">
let mut letters = String::new();
</syntaxhighlight>
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
const MEANING_OF_LIFE:u8 = 42;
// Add a char
let a = 'a' as u8;
letters.push(a as char);
 
// String to str (string slice)
let u:%str = &letters;
 
// Concatenation
let z = lettters + &letters
 
// Other examples
let mut abc = "hello world".to_string()'
abc.remove(0);
abc.push_str("!!!");
abc.replace("ello","goodbye")
</syntaxhighlight>
</syntaxhighlight>
Static const
 
==Hashmap==
Reminds me of my C++ and Java days. No surprises here for reference
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
static Z:i32 = 123;
let mut basket = HashMap::new();
 
basket.insert(String::from("banana"), 2);
basket.insert(String::from("pear"), 2);
basket.insert(String::from("peach"), 2);
</syntaxhighlight>
</syntaxhighlight>
Updating was a bit more tricky than expected. This was the copilot approach
<syntaxhighlight lang="rust">
struct TeamScores {
    goals_scored: u8,
    goals_conceded: u8,
}


== Stack and Heap ==
fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
Same a c++ i.e.  
    // The name of the team is the key and its associated struct is the value.
    let mut scores = HashMap::new();
 
    for line in results.lines() {
        let mut split_iterator = line.split(',');
        // NOTE: We use `unwrap` because we didn't deal with error handling yet.
        let team_1_name = split_iterator.next().unwrap();
        let team_2_name = split_iterator.next().unwrap();
        let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap();
        let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();
 
        let team_1 = scores.entry(team_1_name).or_insert(TeamScores::default());
        team_1.goals_scored += team_1_score;
        team_1.goals_conceded += team_2_score;
 
        let team_2 = scores.entry(team_2_name).or_insert(TeamScores::default());
        team_2.goals_scored += team_2_score;
        team_2.goals_conceded += team_1_score;
    }
 
    scores
}
</syntaxhighlight>
The suggestion was to use get_mut on hashmap but struggle to get this to work. The solution from Chris biscardi on youtube was this, clearly the rust team looked at this and did it better.
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let y = Box::new(10);
fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
println!("y = {}", *y);
...
        scores
            .entry(team_1_name)
            .and_modify(|team: &mut TeamScores| {
                team.goals_scored += team_1_score;
                team.goals_conceded += team_2_score;
            })
            .or_insert(TeamScores {
                goals_scored: team_1_score,
                goals_conceded: team_2_score,
            });
 
        scores
            .entry(team_2_name)
            .and_modify(|team: &mut TeamScores| {
                team.goals_scored += team_2_score;
                team.goals_conceded += team_1_score;
            })
            .or_insert(TeamScores {
                goals_scored: team_2_score,
                goals_conceded: team_1_score,
            });
 
</syntaxhighlight>
</syntaxhighlight>


= Control Flow =
= Control Flow =
== if statement ==
== if statement ==
Same as C++ except no brackets
Same as C++ except no brackets
Line 142: Line 309:
There is support for continue and break
There is support for continue and break


=== Loop ===
===Loop===
Loop is while true
Loop is while true
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
Line 158: Line 325:
     println!("x = {}",x);
     println!("x = {}",x);
  }
  }
</syntaxhighlight>
// You can get position in series as well
 
You can get position in series as well
<syntaxhighlight lang="rust">
  for (pos,x) in (1..11).enumerate()
  for (pos,x) in (1..11).enumerate()
  {
  {
     println!("x = {}, pos = {}",x, pos);
     println!("x = {}, pos = {}",x, pos);
  }
  }
</syntaxhighlight>
</syntaxhighlight>


== Match ==
=Rust Principles=
Match can be used like case
==Ownership==
==Move==
Move is when you assign a value to another variable. If we try and use a variable after the move we will get an error.
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let country = match country_code
let v = vec![1,2,3]
{
let v2 = v;
    44 => "uk",
println!("{:?}",v2)
    46 => "sweden",
println!("{:?}",v) // Error
     7 => "russia"
</syntaxhighlight>
    1...999 => "unknown" // other triple dot inclusive
 
     _ => "invalid" // invalid
==Copy==
};
When we copy something me make a new thing. They is not the same a let a = b, which is assignment. Copy means we duplicate the underlying data of the type. For primitives a copy is implemented by default. This is because the primitive has a know size. E.g. u32, bool etc. If you want to be able to copy a non primitive you need to add the derive macro. Note Clone must also be specified
<syntaxhighlight lang="rust">
#[derive(Copy, Clone)]
enum Direction {
     North,
    East,
    South,
    West,
}
 
#[derive(Copy, Clone)]
struct RoadPoint {
    direction: Direction,
    index: i32,
}
</syntaxhighlight>
 
==Clone==
Clone is a method you can call on a struct if you want a second instance and not move the ownership. Here is an example. The struct obviously needs to implement the Copy/Clone macro. Cloning clearly increases the memory used.
<syntaxhighlight lang="rust">
let v = vec![1,2,3]
let v2 = v.cone();
println!("{:?}",v)
println!("{:?}",v2)
</syntaxhighlight>
 
==References==
So references are like C++ references, but for rust this means you can pass the ownership during function call
<syntaxhighlight lang="rust">
main() {
     let mut s = String::from("Hello");
    change_string(&mut s);
}
 
fn change_string(some_string: &mut String) {
    some_string.push_str(", world!");
}
</syntaxhighlight>
</syntaxhighlight>
'''Note for returning a Reference'''<br>
If we are returning a reference we must be returning a parameter as all local variables are destroyed. (Clearly Rust is not going to allow new MyMemory(6502)


=Data Structures=
=Structs=
==Structs==
Struct are the nearest thing to roughly classes in rust. You make a struct and then add implementation which are methods
==General==
There are 3 types of structs, name, tuple and unit structs
There are 3 types of structs, name, tuple and unit structs
===Name Struct===  
*Named
*Tuples
*Unit
===Name Struct===
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
struct User
struct User
Line 208: Line 417:
</syntaxhighlight>
</syntaxhighlight>


===Tuple Struct===  
===Tuple Struct===
Tuple structs use the order in which declared to assign.
Tuple structs use the order in which declared to assign.
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
Line 214: Line 423:
   let coords = Coordinates{1,2,3};
   let coords = Coordinates{1,2,3};
</syntaxhighlight>
</syntaxhighlight>
 
===Unit Struct===
===Unit Struct===  
These are used to mark the existence of something
Need to understand this example is
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
struct UnitStruct;
struct UnitStruct;
let a = UnitStruct{}
</syntaxhighlight>
</syntaxhighlight>
The example shown was when you are implementing a trait (interface) but the properties were not required for this type. So given a trait for Area, Square uses size but Point does not have an area as it is zero
<syntaxhighlight lang="rust">
trait AreaCalculator {
  fn calc_area(&self) => f64
}


===Other Examples===  
struct Square {
  size: f64
}
 
struct Point;
 
impl AreaCalculator for Square {
  fn calc_area(&self) -> f64 {
    self.size * self.size
  }
}
 
impl AreaCalculator for Point {
  fn calc_area(&self) -> f64 {
    0.0
  }
}
</syntaxhighlight>
We can use it for error
<syntaxhighlight lang="rust">
struct DivideByZero;
 
fn divide(nom: f64, den: f64) -> Result<f64, DivideByZero> {
  if den != 0.0 {
      Ok(nom/den)
  } else {
      Err(DivideByZero)
  }
}
</syntaxhighlight>
==Example Structs==
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
struct Point
struct Point
Line 233: Line 477:
   let p = Point { x: 30.0, y: 4.0 };
   let p = Point { x: 30.0, y: 4.0 };
   println!("point is at ({},{})", p.x, p.y)
   println!("point is at ({},{})", p.x, p.y)
}
</syntaxhighlight>
==Methods on Structs==
Methods on struct require the first argument to be self
===Example Method===
Add method len to struct
<syntaxhighlight lang="rust">
struct Line
{
start: Point,
end: Point
}
// Declare impl using the keyword impl. Not ends with no semi colon.
impl Line
{
  fn len(&self) -> f64
  {
    let dx = self.start.x - self.end.x;
    let dy = self.start.y - self.end.y;
    (dx*dx+dy*dy).sqrt()
  }
}
</syntaxhighlight>
===Changing an attribute===
To change an attribute and ensure you do not break the borrowing rules we do
<syntaxhighlight lang="rust">
struct Square {
  width: u32,
  height: u32
}
impl Square
{
  fn area(&self) -> u32 {
    self.width * self.height
  }
  fn change_width(&mut self, new_width: u32) -> Self
  {
    self.width = new_width;
  }
}
...
main() {
...
  let mut sq = Square(width:5, height: 5);
  sq.change_point(10)
}
</syntaxhighlight>
=Lifetime=
==What are Dangling References==
The code below will not compile. This is because x goes out of scope before r. I am guessing this is what is known as a dangling reference.
<syntaxhighlight lang="rust">
fn test() {
    let r;
    {
        let x = 5;
        r = &x; // Error `x` does not live long enough
    }
    log::info!("{}",r);
}
</syntaxhighlight>
==Lifetime Annotations==
Not sure which way around these are but you specify lifetime annotations on functions and structs and they imply information to the compiler on how long the parameters will live for.
==Three Rules of Lifetimes==
Here are the rules but we also need to understand what they apply to. Kind of chicken and egg. An example is give below which is broken because these rules are not followed.
#Each Parameter that is a reference gets its own lifetime parameter
#If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters
#If there are multiple input lifetime parameters, but one of them is &self or &mut self the lifetime is assigned to all output lifetime parameters
==Example (Broken code)==
Here is an example of code which cannot be compiled without lifetime being specified.
<syntaxhighlight lang="rust">
pub struct TestStruct {
    length: i32,
}
fn test2(x: &TestStruct, y: &TestStruct) -> &TestStruct { // Missing lifetime specifier
    if x.length > y.length {
      x
    }
    else {
      y
    }
}
</syntaxhighlight>
==Adding Annotations==
To do this we specify annotations. The extension in vscode does this for us using the quick fix. The code now looks like this
<syntaxhighlight lang="rust">
fn test2<'a>(x: &'a TestStruct, y: &'a TestStruct) -> &'a TestStruct {
    if x.length > y.length {
      x
    }
    else {
      y
    }
}
</syntaxhighlight>
My inference from this is that all parameters have the same lifetime.
==Lifetime Annotations for Structs==
Structs can also have lifetime annotations. If you specify a reference then you will need to specify a lifetime annotation. In the example below when we make the struct of type MyString we need to make sure that str1 does not go out of scope while x of type MyString exists otherwise it would refer to something no longer in scope.
<syntaxhighlight lang="rust">
// Without lifetime annotation will not compile.
// struct MyString {
//  text: &str,
// }
struct MyString<'a> {
  text: &'a str,
}
fn main() {
    let str1 = String::from("This is my String);
    let x = MyString(text: str1.as_str());
}
}
</syntaxhighlight>
</syntaxhighlight>


== Enumerators ==
==Static Lifetimes==
=== Similar to c++ ===
We can also have lifetimes for statics.
<syntaxhighlight lang="rust">
let s: &'static str = "I live forever";
</syntaxhighlight>
Doing this means the values are stored in  the binary.
=Enums=
==Example 1 with Method==
Seems a bit C++ but... we can add a method as we do with structs. Note for this method we are returning something and with rust all locals are destroyed on return so we need to specify a lifetime.
<syntaxhighlight lang="rust">
enum Pet {dog, cat, fish}
 
impl Pet {
  fn what_am_i(self) -> &'static str {
      match self {
        Pet::dog => "I am a dog",
        Pet::cat => "I am a cat",
        Pet::fish => "I am a fish", 
      }
  }
}
</syntaxhighlight>
 
==Example 2==
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
enum Color {
enum Color {
Line 256: Line 643:
</syntaxhighlight>
</syntaxhighlight>


=== But maybe not we can add types ===
==Example 3 with Types==
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
enum Color {
enum Color {
Line 287: Line 674:
}
}
</syntaxhighlight>
</syntaxhighlight>
 
==Option<T> Enum==
== Option <T> and if let ==
This enum if provided for us by rust and looks like this
Used to avoid null or invalid values
<syntaxhighlight lang="rust">
enum Option<T> {
    None,
    Some(T)
}
</syntaxhighlight>
We would choose this type when we have a case where there could be a value or not. I guess this is the equivalent of string? in Typescript where we may or may not have a value. In rust we use match to support this type.
<syntaxhighlight lang="rust">
  let some_number = Some(5);
  let some_string = Some("a string");
  let nothing: Option<i32> = None;
</syntaxhighlight>
==Pattern Matching==
Match is '''Exhaustive approach''' to pattern matching. I.E. you need to specify something for every option you are using match for. However you can include a default. I find this a great approach
===Examples===
====Simple Match====
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let x = 3.0
match x
let y = 0.0 // Divide by zero
 
let result:Option<f64> =
  if y != 0.0 { Some(x/y) } else { None };
 
// Using match
match result
{
{
  Some(z) => println!("Goody result"),
  0 => "zero"
  None => println!("No result)
  1 | 2 => "one or two"
  9...11 => "lots of"  // two dots does not include end value (exclusive)
  _ if(blahh) => "something"
  _ => "all others"
}
}
// Using if let
if let Some(z) = result { println!("z = {}", z); }
</syntaxhighlight>
</syntaxhighlight>
 
Here is another example.
== Arrays ==
Array sizes cannot grow in rust
=== Simple ===
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let mut a:[i32;5] = [1,2,3,4,5];
let country = match country_code
// Or
{
let mut a = [1,2,3,4,5];
    44 => "uk",
// Length
    46 => "sweden",
a.len()
    7 => "russia"
// Assignment
    1...999 => "unknown" // other triple dot does include end value (inclusive)
a[0] = 321
    _ => "invalid" // invalid
// Printing
};
println!("{:?}", )
// Testing
  if a == [1,2,3,4,5]
  {
  }
// Initialise
  let b = [1,10]; // 10 array initialised to 1
 
</syntaxhighlight>
</syntaxhighlight>
=== Multi Dimension ===
This just shows inclusive which is ..= unlike kotlin which I think is 3 dots
Here is a two dimension array
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let mtx:[[f32;3];2] =
// This function returns how much icecream there is left in the fridge.
[
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
  [1.0, 0.0, 0.0],
// someone eats it all, so no icecream is left (value 0). Return `None` if
  [0.0, 2.0, 0.0],
// `hour_of_day` is higher than 23.
];
fn maybe_icecream(hour_of_day: u16) -> Option<u16> {
 
    match hour_of_day {
        0..22 => Some(5),
        22..=23 => Some(0),
        _ => None,
    }
}
</syntaxhighlight>
</syntaxhighlight>


== Vectors ==
====More Complex====
Same a c++
Stumped me when see thing for the first time prior to type script and possibly lambda. Here we define anonymous functions which match the type of the enum. Here is the enum which is used in another struct
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let mut a = Vec::new()
enum Message {
a.push(1);
    Move(Point),
a.push(2);
    Echo(String),
a.push(3);
    ChangeColor(u8, u8, u8),
// Print
    Quit,
println!("a[0] {}", a[0]);
    Resize { width: u64, height: u64 },
 
// We can create vector with initial capacity
let mut b = Vec::<i32>::with_capacity(2);
 
// We can initialize using an iterator values of 0-4
let c: Vect<i32> = (0..5).collect();
 
// Using get returns a option
match a.get(3333)
{
...
}
 
// Removing, pop returns an option 
let last_elem = a.pop();
 
// Using the option type iterating over vector to print it
while let Some(x) = a.pop()
{
  println!("x = {}",x);
}
}
</syntaxhighlight>
</syntaxhighlight>
 
It has functions for each enum type.
= More Data Structures =
== Slices ==
A slice is a non-owning pointer to a block of memory. For example
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
struct State {
    width: u64,
    height: u64,
    position: Point,
    message: String,
    // RGB color composed of red, green and blue.
    color: (u8, u8, u8),
    quit: bool,
}


// Create a vector
impl State {
let v: Vec<i32> = {0..5}.collect();
    fn resize(&mut self, width: u64, height: u64) {
        self.width = width;
        self.height = height;
    }


// Now create a slice (reference)
    fn move_position(&mut self, point: Point) {
let sv: &[i32]= &v;
        self.position = point;
    }


// We create a slice with only some elements
    fn echo(&mut self, s: String) {
let sv1: &[i32]= &v[2..4];
        self.message = s;
    }


// Printing these will produce the same result
    fn change_color(&mut self, red: u8, green: u8, blue: u8) {
println!("{:?}",v);
        self.color = (red, green, blue);
println!("{:?}",sv);
    }


// And the range
    fn quit(&mut self) {
println!("{:?}",sv1);
        self.quit = true;
    }


    fn process(&mut self, message: Message) {
...
    }
}
</syntaxhighlight>
</syntaxhighlight>
At first I struggled to understand how to implement process but all you need to do is provide an ()_=> {} for each type. For Quit I completely understood but for the others was confused. Obvious once you know and I am sure copilot will do this for me
Get the first 3 elements of an array
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
 
    fn process(&mut self, message: Message) {
fn use_slice(slice: &mut[i32])
        match message {
{
            Message::Move(point) => self.move_position(point),
 
            Message::Echo(output) => self.echo(output),
}
            Message::ChangeColor(red, green, blue) => self.change_color(red, green, blue),
 
            Message::Quit => self.quit(),
fn test()
            Message::Resize { width, height } => self.resize(width, height),
{
        }
  let mut data = [1,2,3,4,5];
    }
  // Passes element 1-3 to use_slice as a reference
  use_slice( &mut data[1..4]);
}
</syntaxhighlight>
</syntaxhighlight>


== Strings ==
====Match on Tuples====
Basic String
This is an exert from [[https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life Game of Life]]. We can match on tuples, and I imagine other types too. For tuples you can specify a value or compare to a value. Note the use of '''otherwise'''
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let name = String::from("Iain");
    let next_cell = match (cell, live_neighbors) {
      // Rule 1: Any live cell with fewer than two live neighbours
      // dies, as if caused by underpopulation.
      (Cell::Alive, x) if x < 2 => Cell::Dead,
      // Rule 2: Any live cell with two or three live neighbours
      // lives on to the next generation.
      (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,
      // Rule 3: Any live cell with more than three live
      // neighbours dies, as if by overpopulation.
      (Cell::Alive, x) if x > 3 => Cell::Dead,
      // Rule 4: Any dead cell with exactly three live neighbours
      // becomes a live cell, as if by reproduction.
      (Cell::Dead, 3) => Cell::Alive,
      // All other cells remain in the same state.
      (otherwise, _) => otherwise,
};
</syntaxhighlight>
</syntaxhighlight>


Two types, static string and string type
===Operators and Symbols===
Found in Table B-1 here  [[https://doc.rust-lang.org/book/appendix-02-operators.html Operators and Symbols]]
#[[https://doc.rust-lang.org/std/ops/struct.Range.html Range]]: 1..10
#[[https://doc.rust-lang.org/std/ops/struct.RangeFrom.html RangeFrom]]: 1..
#[[https://doc.rust-lang.org/std/ops/struct.RangeTo.html RangeTo]]: ..10
#RangeFull: ..
#RangeInclusive: 1..=10
#RangeToInclusive: ..=10
 
==Option <T> and if let==
Used to avoid null or invalid values. This was used in things where the value might be present. Maybe command line arguments where some were provide or none were provided. Lets to the classic divide by zero.
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let s = "hello";
let x = 3.0
// Cannot do
let y = 0.0 // Divide by zero
// let h = s[0]
 
// You can iterate as a sequence using chars e.g.
let result:Option<f64> =
for c in s.chars()
  if y != 0.0 { Some(x/y) } else { None };
{
 
  println!("{}", c);
// Using match
match result {
  Some(z) => println!("Goody result"),
  None => println!("No result")
}
}
// Using if let
if let Some(z) = result { println!("z = {}", z); }
</syntaxhighlight>
</syntaxhighlight>
And now the mutable string in rust essentially an vector
== More if let ==
// Create a string
Here is another example
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let mut letters = String::new();
let mut stack = Vec:new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
  println!("{}", top);
}
</syntaxhighlight>
</syntaxhighlight>
Add a char
==while let ==
The above example makes great sense but while doing rustlings the was this question
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let a = 'a' as u8;
  // TODO: Make this a while-let statement. Remember that `Vec::pop()`
letters.push(a as char);
  // adds another layer of `Option`. You can do nested pattern matching
  // in if-let and while-let statements.
  integer = optional_integers.pop() {
    assert_eq!(integer, cursor);
    cursor -= 1;
  }
</syntaxhighlight>
</syntaxhighlight>
String to str
I did like the Some Some approach
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let u:%str = &letters;
  while let Some(Some(integer)) = optional_integers.pop() {
    assert_eq!(integer, cursor);
    cursor -= 1;
  }
</syntaxhighlight>
</syntaxhighlight>
Concatenation
But could not get the '''You can do nested pattern matching in if-let and while-let statements''' to look nice
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let z = lettters + &letters
  while let Some(integer) = if let Some(integer) = optional_integers.pop() {
    integer
  } else {
    None
  } {
    assert_eq!(integer, cursor);
    cursor -= 1;
  }
</syntaxhighlight>
</syntaxhighlight>
Other examples
Just wouldn't let it lie, found a way to turn it up the right way
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let mut abc = "hello world".to_string()'
  while let Some(integer) = optional_integers.pop() {
abc.remove(0);
    if let Some(integer) = integer {
abc.push_str("!!!");
      assert_eq!(integer, cursor);
abc.replace("ello","goodbye")
      cursor -= 1;
</syntaxhighlight>
    }
 
  }
== Tuples ==
Eezy peezy lemon squeezy
<syntaxhighlight lang="rust">
fn sum_and_product(x:i32,y:i32) -> (i32, i32)
{
(x+y, x*y)
}
 
fn main()
{
  let sp = sum_and_product(3,4);
  let (a,b) = sp;
  let sp2 = sum_and_product(4,5);
 
  // combine
  let combined = (sp, sp2);
  let ((c,d), (e,f)) = combined;
}
</syntaxhighlight>
== Pattern Matching ==
Various pattern matching
<syntaxhighlight lang="rust">
match x
{
  0 => "zero"
  1 | 2 => "one or two"
  9...11 => "lots of"
  _ if(blahh) => "something"
  _ => "all others"
}
</syntaxhighlight>
</syntaxhighlight>
Using two dots .. with pattern matching allows you to ignore other values except those specified.


== Generics ==
=Generics=
Looks like templates
==Simple==
This is very similar to C++ Templates and TypeScript Generics
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
struct Point<T>
struct Point<T>
Line 501: Line 904:
}
}
</syntaxhighlight>
</syntaxhighlight>
 
==Using Implementation==
= Functions =
An example where we use an implementation
== Functions and Arguments ==
No surprises
<syntaxhighlight lang="rust">
fn print_value(x: i32)
{
  println("value = {}", x);
}
</syntaxhighlight>
Pass by reference
<syntaxhighlight lang="rust">
fn increase(x: &mut i32)
{
  *x = 1;
}
</syntaxhighlight>
Return value, note no semicolon
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
fn product(x: i32, y: i32) -> i32
struct Wrapper<T> {
{
    value: T,
  x * y
}
}
</syntaxhighlight>
Return two values, note no semicolon
<syntaxhighlight lang="rust">
fn product(x: i32) -> i32
{
  if x == 10 {
    123
  }
 
  321
}
</syntaxhighlight>
== Methods on Structs ==
Add method len to struct
<syntaxhighlight lang="rust">
struct Line
{
start: Point,
end: Point
}
// Declare impl using the keyword impl. Not ends with no semi colon.
impl Line
{
  fn len(&self) -> f64
  {
    let dx = self.start.x - self.end.x;
    let dy = self.start.y - self.end.y;
    (dx*dx+dy*dy).sqrt()
  }
}
</syntaxhighlight>
== Closures ==
Closures are delegates. Here is an inline one
<syntaxhighlight lang="rust">
let plus_one = |x:i32| -> i32 { x + 1};
let a = 6;
println!("{}, + 1 = {}",a plus_one(a));
// another approach
let plus_to = |x|
{
let mut z = x;
z += 2;
z
};
</syntaxhighlight>
We need to ensure scope is executed before using variables again e.g.
<syntaxhighlight lang="rust">
let mut two = 2;
let plus_to = |x|
{
let mut z = x;
z += two;
z
};
let borrow_two = &mut two;
</syntaxhighlight>
This will not compile as two could be modified so you need to add bracket to ensure scope.
<syntaxhighlight lang="rust">
let mut two = 2;
{
  let plus_to = |x|
  {
  let mut z = x;
  z += two;
  z
  };
}
let borrow_two = &mut two;
</syntaxhighlight>
== Higher-order functions ==
Not sure what this is, seems like just a way to chain written functions together like lamba. Here is the given example.
<syntaxhighlight lang="rust">
fn is_even(x: u32) -> bool
{
  x%2 == 0
}
fn main()
{
// Method without HOF
  let limit = 500;
  let mut sum = 0;
  for i in 0..
  {
    let isq = i*i;
    if isq > limit { break; }
    else if is_even(isq) { sum += isq; }
  }
  println!("loop sum = {}", sum);
// HOF way


  let sum2 =
impl<T> Wrapper<T> {
    (0...).map(|x| x*x)
    fn new(value: T) -> Self {
        .take_while(|&x| x < limit)
         Wrapper { value }
        .filter(|x| is_even(*x))
    }
         .fold(0, |sum,x| sum+x);
 
  println!("hof sum = {}", sum2);
}
}
</syntaxhighlight>
</syntaxhighlight>


== Traits ==
=Traits=
Traits are interfaces from maybe c#
Traits are similar to interfaces in java and c#
==Defining a Traits==
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
trait Animal
trait Animal
Line 653: Line 933:
   }
   }
}
}
 
</syntaxhighlight>
==Implement a Trait==
Here we create a struct which will implement out trait. Note we do not have to implement all functions if the trait provides a default implementation
===Implement a Trait for Animal===
<syntaxhighlight lang="rust">
struct Human
struct Human
{
{
Line 659: Line 943:
}
}


// Implement interface
impl Animal for Human
impl Animal for Human
{
{
Line 677: Line 960:
   }
   }
}
}
 
</syntaxhighlight>
===Implement a Trait for Cat===
Here we implement the Animal Trait for Cat
<syntaxhighlight lang="rust">
struct Cat
struct Cat
{
{
Line 702: Line 988:
}
}


// Create doing


// Usage
let h:Human = Animal::create("John");
let h:Human = Animal::create("John");
let c:Cat = Animal::create("John");
let c:Cat = Animal::create("John");
</syntaxhighlight>
</syntaxhighlight>
= Lifetime =
 
== Ownership ==
===Default Trait and Spread===
Only one thing can have ownership
For a struct we can create a default for it. We can use a typescript like spread operator (although it must be last) for override these defaults
<syntaxhighlight lang="rust">
pub struct Circle {
    color: String,
    point: Point,
    radius: u16,
}
 
impl Circle {
    pub fn new(color: String, point: Point, radius: u16) -> Circle {
        Circle {
            color,
            point,
            radius,
        }
    }
 
    pub fn default_color(point: Point, radius: u16) -> Circle {
        Circle {
            point,
            radius,
            ..Default::default()
        }
    }
}
 
impl Default for Circle {
    fn default() -> Self {
        Circle {
            color: String::from("black"),
            point: Point::new(0, 0),
            radius: 0,
        }
    }
}
 
// Default Circle
let circle = Circle::default();
 
// Default Black Circle
let circle = Circle::default_color(Point::new(1, 1), 1);
</syntaxhighlight>
 
===Traits and with Dyn and Impl===
To allow any struct which implements the trait we use the '''dyn''' or '''impl''' keyword
====Difference Between dyn and impl====
*'''dyn''' means dynamic dispatch. This results in a fat pointer. One pointer pointing to the data and the other pointing to the vtable.
*'''impl''' means static dispatch. This results in a new copy of the function for each usage
====Example====
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let v = vec![1,2,3]
trait Licensed {
let v2 = v;
    fn licensing_info(&self) -> String {
println!("{:?}",v)
        "Default license".to_string()
    }
}
 
struct SomeSoftware;
struct OtherSoftware;
 
impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}
 
// TODO: Fix the compiler error by only changing the signature of this function.
fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool {
    software1.licensing_info() == software2.licensing_info()
}
 
// Now we can do this
compare_license_types(SomeSoftware, OtherSoftware)
compare_license_types(OtherSoftware, SomeSoftware)
</syntaxhighlight>
====Why Box====
When can return a dyn Trait too. We clearly do not know the size of the return value. To overcome this we put the return argument in a Box which is essentially a smart pointer in C++
<syntaxhighlight lang="rust">
fn random_animal(random_number: f64) -> Box<dyn Animal> {
    if random_number < 0.5 {
        Box::new(Sheep {})
    } else {
        Box::new(Cow {})
    }
}</syntaxhighlight>
 
===Super Traits===
We can make a trait to which says you must implement these traits to implement me.
<syntaxhighlight lang="rust">
trait Amphibious : WaterCapable + LandCapable + UnderWaterCapable {} 
</syntaxhighlight>
</syntaxhighlight>
Only v can own the vector so this will not compile. Upon usage you get the error "use of moved value"
Now when you use this trait you have to implement the other 3 traits.
== Move ==
 
If we change the code to use v2 instead. This is OK because we have successfully moved the ownership to v2
==Provided Traits==
<syntaxhighlight lang="rust" highlight="3">
===Drop Trait===  
let v = vec![1,2,3]
Drop trait is called automatically to free up resources but you can write your own e.g. for the example above we could write
let v2 = v;
<syntaxhighlight lang="rust">
println!("{:?}",v2)
impl Drop for Course {
  fn drop(&mut self) {
    println("Dropping")
  }
}
</syntaxhighlight>
</syntaxhighlight>
==Clone==
===Clone Trait===
This is a way to duplicate the value to fix this. Obviously this has a cost
Like the drop trait we can implement our own. Refer to the clone trait for this.  
===Copy Trait===
We can either specify #[derive(Copy, Clone)] or implement our own. There are restrictions on this
===From and Into Trait===
This allow us to convert from one type to another
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let v = vec![1,2,3]
fn into(self) -> T
let v2 = v.cone();
fn from(T) ->  Self
println!("{:?}",v)
fn try_into(self) -> Result<T, Self: Error>
println!("{:?}",v2)
fn try_from(value: T) -> Result<Self, Self: Error>
</syntaxhighlight>
</syntaxhighlight>
==Copy==
==Trait Bounds 1==
Primitive types and others can implement copy which would allow the automatic duplication of a type. Here is i32.
In order to allow use of more than on trait in a function we can use the +. This example means that item must implement both traits, i.e. SomeTrait and OtherTrait
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let v = 1
fn some_func(item: impl SomeTrait + OtherTrait) -> bool {
let v2 = v
    item.some_function() && item.other_function()
println!("{:?}",v)
}
println!("{:?}",v2)
</syntaxhighlight>
</syntaxhighlight>
==References==
==Trait Bounds 2==
We can pass references to an object using mut to modify a string.
Here is an example of doing the same thing in two ways. Because we can have anything in grade (T) we must make an implementation for std::fmt::Display. That way if we make a ReportCard with a generic which does not support Display, it will not compile
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
...
 
  let mut s = String::from("hello");
struct ReportCard<T> {
  change_string(&mut s);
    grade: T,
  printlin!("{}",s)
    student_name: String,
    student_age: u8,
}
 
// Approach 1
impl<T> ReportCard<T>
where
    T: std::fmt::Display,
{
    fn print(&self) -> String {
        format!(
            "{} ({}) - achieved a grade of {}",
            &self.student_name, &self.student_age, &self.grade,
        )
    }
}
}


fn change_string(some_string: &mut String) {
// Approach 2
  some_thing.push_str(", world!");
impl<T: std::fmt::Display> ReportCard<T> {
    fn print(&self) -> String {
        format!(
            "{} ({}) - achieved a grade of {}",
            &self.student_name, &self.student_age, &self.grade,
        )
    }
}
}
</syntaxhighlight>
</syntaxhighlight>


== Borrowing ==
==Trait Bounds==
This fails because we are have two mutable accesses to a
The example above has two ways to achieve the same thing. If we constrain what this allowed, this is called trait bounds. Lets add a second parameter.
<syntaxhighlight lang="rust">
// This example only forces the struct to implement the trait
// fn overview(item1: &imp Overview, item2: &imp Overview)
 
// But this force the struct to be of the same type
// fn overview<T: Overview>(item1: &T, item2: &T)
</syntaxhighlight>
We can add more constraints with the + operator. Now they need the second trait.
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let mut a = 40;
// fn overview(item1: &imp Overview + AnotherTrait, item2: &imp Overview + AnotherTrait)
let b &mut a
// fn overview<T: Overview + AnotherTrait>(item1: &T, item2: &T)
*b += 2;
println!("a = {}", a);
</syntaxhighlight>
</syntaxhighlight>
This fails because we are have two mutable accesses to a
Here we have an example of ensuring that the incoming parameters are constrained to be of type T
<syntaxhighlight lang="rust">
struct Pointy<T> {
    x: T,
    y: T,
}


Same fix as before
impl <T> Add for Pointy <T>
where T: Add<Output = T>
{
    type Output = Self;
 
    fn add(self, other: Self) -> Self {
        Self {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}
</syntaxhighlight>
 
==Passing Trait as Parameters==
So here is an example of two structs with overview implement,one using the trait default implementation, the other its own. We can use the trait similar to a pointer to a function.
==Example==
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
let mut a = 40;
trait Overview {
{
  fn overview(&self) -> String {
   let b &mut a
    format("This is a rust course")
  *b += 2;
  }
}
 
struct Course {
  headline: String,
  author: String
}
 
struct AnotherCourse {
  headline: String,
  author: String
}
 
impl Overview for Course {
}
 
impl Overview for AnotherCourse {
   fn overview(&self) -> String {
    format("{}, {}", self.author, self.headline)
  }
}
 
</syntaxhighlight>
We can use the overview trait a a fn parameter with
<syntaxhighlight lang="rust">
fn call_overview(item: &imp) {
  println("Overview: {}", item.overview())
}
 
// OR
fn call_overview<T: Overview>(item: &T) {
  println("Overview: {}", item.overview())
}
}
println!("a = {}", a);
 
</syntaxhighlight>
</syntaxhighlight>
= Odds and Ends =
==Passing Traits (From Youtube)==
== Consuming Crates ==
Taken from Youtube and repeated. There are two notations for passing a trait. These are the same but the first is perhaps more readable. The second is known as a trait bound.
Crates is like nuget [https://crates.io/]
<syntaxhighlight lang="rust">
<syntaxhighlight lang="toml">
pub fn foo (traitor: &impl SpiDevice) {
[package]
 
name = "mypackage"
}
version = "0.1.0"
 
author = " ["Iain Wiseman iwiseman@bibble.co.nz"]
pub fn foo<T: SpiDevice>(traitor: &T) {


[dependencies]
}
rand = "0.3.12"
</syntaxhighlight>
With the impl syntax we can make the parameter have more the one trait with a plus.
<syntaxhighlight lang="rust">
pub fn foo (traitor: &impl SpiDevice + AnotherTrait) {


}
</syntaxhighlight>
</syntaxhighlight>
And the usage
With the second syntax if we have two parameters if allows us to make sure they both share the same trait easily as the type is only specified once
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
extern crate rand; // Package
pub fn foo<T: SpiDevice>(traitor1: &T, traitor2) {
use rand::Rng; // Namespace


fn main() {
  let mut rng = rand::thread_rng();
  let b:bool = rng.gen();
}
}
</syntaxhighlight>
</syntaxhighlight>
== Building Crates and Modules ==
We can also add a second trait with this syntax too.
Module example e.g. src/lib.rs
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
pub mod greetings
pub fn foo<T: SpiDevice + AnotherTrait>(traitor1: &T, traitor2) {
{
 
  pub mod english
  {
      pub fn hello() -> {"hello"->to_string() }
      pub fn goodbye() -> {"goodbye"->to_string() }
  }
  pub mod french
  {
      pub fn hello() -> {"bonjour"->to_string() }
      pub fn goodbye() -> {"au revoir"->to_string() }
  }
}
}
</syntaxhighlight>
</syntaxhighlight>
For modules within modules you can make directories. For above this would be greetings\english and greetings\french. So we could have greetings\english\english.rs
This starts to get messy to we can tidy this up with the Where Clause
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
  pub fn hello() -> {"hello"->to_string() }
pub fn foo<T, U>(traitor1: &T, traitor2: &U) -> i32
  pub fn goodbye() -> {"goodbye"->to_string() }
where
    T: SpiDevice + AnotherTrait,
    U: AnotherTrait + YetAnotherTrait
        {
            42
        }
 
</syntaxhighlight>
</syntaxhighlight>


greetings\lib.rs
==Returning Traits (From Youtube)==
We can also return traits but you cannot return different types which share the same trait at this time.
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
pub mod greetings
pub fn foo() -> SpiDevice {
    // Must be of same type
}
</syntaxhighlight>
 
=Common Collections=
==Vectors==
Same a c++
<syntaxhighlight lang="rust">
let mut a = Vec::new()
a.push(1);
a.push(2);
a.push(3);
// Print
println!("a[0] {}", a[0]);
 
// We can create vector with initial capacity
let mut b = Vec::<i32>::with_capacity(2);
 
// We can initialize using an iterator values of 0-4
let c: Vect<i32> = (0..5).collect();
 
// Using get returns a option
match a.get(3333)
{
{
  pub mod english;
...
  pub mod french
}
  {
 
      pub fn hello() -> {"bonjour"->to_string() }
// Removing, pop returns an option 
      pub fn goodbye() -> {"au revoir"->to_string() }
let last_elem = a.pop();
  }
 
// Using the option type iterating over vector to print it
while let Some(x) = a.pop()
{
  println!("x = {}",x);
}
}
</syntaxhighlight>
</syntaxhighlight>


We need a package file for it
==Binary Heap==
<syntaxhighlight lang="TOML">
This struct makes sure the highest is at the top. It has a peek function to allow you to peek at values.
[package]
<syntaxhighlight lang="rust">
name = "phrases"  
let mut bHeap = BinaryHeap::new();
version = "0.1.0"
bHeap.push(1);
author = " ["Iain Wiseman iwiseman@bibble.co.nz"]
bHeap.push(18);
bHeap.push(20);
bHeap.push(5);
bHeap.pop();
 
println!("{:?}", bHeap); // 20
 
</syntaxhighlight>
 
==Maps==
Not discussed
==Sets==
Not discussed
=Error Handling=
==Panic==
Panic happens when unhandled error occurs. This happens for instance when we access out of bounds array. We can get a backtrace by setting the environment export RUST_BACKTRACE=-1
==Result Enum==
The Result an enum which has two generics Result<T, E> where T is the type an E is the error. In rust we use the match to determine what to do.
<syntaxhighlight lang="rust">
let file = File::Open("Does_not_exist.mp3");
let file match file {
    Ok(file) => file,
    Err(error) => panic("Error: {:?}", error),
};
</syntaxhighlight>
</syntaxhighlight>
And then cargo build should work. To use the package you will need to specify the path to the package in the cargo file where used. e.g.
<syntaxhighlight lang="toml">
[package]
name = "mypackage"
version = "0.1.0"
author = " ["Iain Wiseman iwiseman@bibble.co.nz"]


[dependencies]
==Mapping Errors==
rand = "0.3.12"
Rust likes you to make your own errors and map the ones you handle to you errors which makes sense. We make our own errors using enums
phrases = { path = "../Phrases" }
<syntaxhighlight lang="rust">
enum ParsePosNonzeroError {
    Creation(CreationError),
    ParseInt(ParseIntError),
}
</syntaxhighlight>
</syntaxhighlight>
Now we can provide helper function to convert from one type of error to ours
<syntaxhighlight lang="rust">
impl ParsePosNonzeroError {
    fn from_creation(err: CreationError) -> Self {
        Self::Creation(err)
    }


== Testing ==
    // TODO: Add another error conversion function here.
Rush comes with it's own assert library marked by #[test] e.g. for the above
    fn from_parse_int(err: ParseIntError) -> Self {
        Self::ParseInt(err)
    }
}
</syntaxhighlight>
Now in our parse function we can map the errors in parse() to our own
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
#[test]
#[derive(PartialEq, Debug)]
fn english_greeting_correct()
struct PositiveNonzeroInteger(u64);
{
 
  assert_eq!("hello", greetings::english::hello());
impl PositiveNonzeroInteger {
    fn new(value: i64) -> Result<Self, CreationError> {
        match value {
            x if x < 0 => Err(CreationError::Negative),
            0 => Err(CreationError::Zero),
            x => Ok(Self(x as u64)),
        }
    }
 
    fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
        // TODO: change this to return an appropriate error instead of panicking
        // when `parse()` returns an error.
        let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
        Self::new(x).map_err(ParsePosNonzeroError::from_creation)
    }
}
}
</syntaxhighlight>
</syntaxhighlight>
== Documentation ==
 
rustdoc is used to generate.
=Testing=
We specify the cfg option and use the assert library
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
//! This module comment
fn sqrt(number: f64) -> Result<f64, String> {
//! and this
    if number >= 0.0 {
//! #Examples are compiled with back ticks
        Ok(number.powf(0.5))
//! ```
    } else {
//! let username = "John";
        Err("negative floats don't have square roots".to_owned())
//! println!("{}", english::hello());
    }
//! ```
}


/// This is for code
#[cfg(test)]
/// In this case, it's our `hello()` function.
mod tests {
pub fn hello() -> String {" hello".to_string() }
    use super::*;


    #[test]
    fn test_sqrt() -> Result<(), String> {
        let x = 4.0;
        assert_eq!(sqrt(x)?.powf(2.0), x);
        Ok(())
    }
}
</syntaxhighlight>
</syntaxhighlight>
=Installing=
We can run these with
Do not use apt as it does not set rustup correctly and then vscode extension will not work with "Couldn't start client Rust Language Server"
<syntaxhighlight lang="bash">
<syntaxhighlight lang="bash">
apt-get install curl build-essential make gcc -y
cargo test
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
</syntaxhighlight>
=Closures=
Closures are functions you which you can use the available scope to with that function. They look like anonymous function in typescript
==Simple Example of a closure==
The adder closure takes a parameter and captures existing scope at creation to produce the answer 17.
<syntaxhighlight lang="rust">
    let x: i32 = 5;
    let adder = |a| a + x;
    let b = adder(12);
    print!("b {}", b)
</syntaxhighlight>
==Mapper Function using Closure==
Here is a closure which is like typescript mapper function using closures and rust
<syntaxhighlight lang="ts">
things.map((element: number) => element * 2)
</syntaxhighlight>
And now the same thing in rust
<syntaxhighlight lang="rs">
things.iter().map(|element| element * 2).collect()
</syntaxhighlight>
==Mapper replacing a for loop using Closure==
The key to do thing was to specify the type with the sum i.e. sum::<usize>() rather than sum()
<syntaxhighlight lang="rust">
fn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize {
    let mut count = 0;
    for val in map.values() {
        if *val == value {
            count += 1;
        }
    }
    count
}
 
fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
    let count = map
        .values()
        .into_iter()
        .map(|x| if *x == value { 1 } else { 0 })
        .sum::<usize>();
    count
}
</syntaxhighlight>
 
==Mapper replacing a two dimensional for loop using Closure==
<syntaxhighlight lang="rust">
fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
    let mut count = 0;
    for map in collection {
        for val in map.values() {
            if *val == value {
                count += 1;
            }
        }
    }
    count
}
 
fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
    // Return a count of all the
    let count: usize = collection
        .iter()
        .map(|x| {
            x.values()
                .into_iter()
                .map(|y| if *y == value { 1 } else { 0 })
                .sum::<usize>()
        })
        .sum();
 
    count
}
</syntaxhighlight>
 
 
==More Complex Example of closure==
We use the existing struct, make a copy and call them inside the  to  pass in objects from somewhere and use their results with the closure function
<syntaxhighlight lang="rust">
fn create_percent_complete_fn<'a>(
  &'a self,
  looper: &'a LooperDuration,
  interpolator: &'a AccelerateDecelerateInterpolator,
) -> impl Fn(u16) -> f64 + 'a {
  let looper_ref = looper;
  let interpolator_ref = interpolator;
  let closure = move |elapsed_time: u16| {
    let rate_of_change = looper_ref.get_t_with_elapsed_time(elapsed_time);
    let b = interpolator_ref.get_interpolar(rate_of_change);
    return b * 100 as f64;
  };
  return closure;
}
</syntaxhighlight>
==Traits provided for closures==
These traits are provided in rust
*Fn
*FnOnce
*FnMut
We need to provide some explanation for this
<syntaxhighlight lang="rs">
/*
  || drop(v)            FnOnce as we only drop once
  |args| v.contains(arg) Fn as does not modify
  |args| v.push(arg)    FnMut as it does modify
*/
</syntaxhighlight>
 
=Iterators=
Nothing fancy here except we have to make the iterator mut (not surprising but worth mentioning). An iterator is any type which implements the iter trait and an iterable is a type that implements into iterator.
<syntaxhighlight lang="rs">
let vec = [1, 2, 3];
let mut iter = vec.iter();
while let Some(i) = iter.next() {
    println!("i {}", i);
}
</syntaxhighlight>
Just like Typescript we can now use closures like the map, filter functions in typescript. E.g. given a vector of times we can filter on property
<syntaxhighlight lang="rs">
items.into_iter().filter(|i|i.name == search_name).collect()
</syntaxhighlight>
We can implement our own iterator on our struct like below
<syntaxhighlight lang="rs">
struct Range {
    start: u32,
    end: u32
}
 
 
impl Iterator for Range {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
      if self.start >= self.end {
          return None
      } 
      let result = Some(self.start);
      self.start += 1;
      result
    }
}
</syntaxhighlight>
==Example 1 Iterators==
This iterates over the input slice, calling next() once. At this point first contains the first item in input and chars now contains the rest. We concatenate the two values.
<syntaxhighlight lang="rs">
fn capitalize_first(input: &str) -> String {
    let mut chars = input.chars();
    match chars.next() {
        None => String::new(),
        Some(first) => {
            let capital = first.to_uppercase();
            format!("{}{}", capital, chars.as_str())
        }
    }
}
</syntaxhighlight>
==Example 2 Iterators Mapping over slice==
This is a good example of mapping like typescript but over a slice
<syntaxhighlight lang="rs">
fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
    words
        .iter()
        .map(|&element| capitalize_first(element))
        .collect::<Vec<_>>()
}
</syntaxhighlight>
 
=Pointers=
This is the same a C++ with some rusty new language. The Box is a pointer type that uniquely owns a heap allocation of type
==Box Type==
This the basic pointer
<syntaxhighlight lang="rs">
let t = (12, "eggs");
let b = Box::new(t);
println!("b {:?}", b); // (12, "eggs")
</syntaxhighlight>
</syntaxhighlight>
=VS Code setup=
==RC Type==
I ended up installing. Still cannot find a in built package manager for cargo<br>
This allows multiple pointers to the same thing in memory. For example
[[File:Rust vscode.png|400px]]
<syntaxhighlight lang="rs">
let s1 = Rc::new(String::from("Pointer));
let s2 = s1.clone();
let s3 = s2.clone();
 
println("{},{},{}",s1,s2,s3) // Pointer, Pointer, Pointer
</syntaxhighlight>
==RefCell==
A RefCell is another way to change values without needing to declare mut. It means "reference cell", and is like a Cell but uses references instead of copies, Rust refers to Interior mutability which seems to say it is a pattern where the user is allow to modify data despite the data not having a mut associated with it. The function of RefCell allow us to modify when we dereferenced.
<syntaxhighlight lang="rs">
use std::{cell::RefCell};
 
struct Flagger {
    is_true: RefCell<bool>,
}
 
// borrow returns Ref<T>
// borrow_mut returns RefMut<t>
 
let flag = flagger{ is_true: RefCell::new(true)};
 
// let reference =  flag.is_true.borrow();
// println!("{}", reference);
 
let mut mut_ref = flag.is_true.borrow_mut();
*mut_ref = false;


=Date Handling=
</syntaxhighlight>
Found the on twitter (not all bad) @orhanbalci Type conversion and dates are the worst in most languages so thought I would keep this.
Note if we want to use it twice, i.e. uncomment the println we have to wrap the RefCell with RC
<syntaxhighlight lang="rs">
<syntaxhighlight lang="rs">
  // In struct
  is_true: Rc<RefCell<bool>>
  // Initialize
  let flag = flagger{ is_true: Rc::new<RefCell::new(true))}; 
</syntaxhighlight>
=Rust Concurrency and Asynchronous Processing=
This can be found [[Rust Concurrency and Asynchronous Processing]]
=Concurrency=
This can be found [[Rust Concurrency and Asynchronous Processing]]
=Macros=
Because this page was so large I moved this to [[Rust_macros]]
= Functions =
== Functions and Arguments ==
No surprises
<syntaxhighlight lang="rust">
fn print_value(x: i32)
{
  println("value = {}", x);
}
</syntaxhighlight>
Pass by reference
<syntaxhighlight lang="rust">
fn increase(x: &mut i32)
{
  *x = 1;
}
</syntaxhighlight>
Return value, note no semicolon
<syntaxhighlight lang="rust">
fn product(x: i32, y: i32) -> i32
{
  x * y
}
</syntaxhighlight>
Return two values, note no semicolon
<syntaxhighlight lang="rust">
fn product(x: i32) -> i32
{
  if x == 10 {
    123
  }
 
  321
}
</syntaxhighlight>
== Higher-order functions ==
Not sure what this is, seems like just a way to chain written functions together like lamba. Here is the given example.
<syntaxhighlight lang="rust">
fn is_even(x: u32) -> bool
{
  x%2 == 0
}
fn main()
{
// Method without HOF
  let limit = 500;
  let mut sum = 0;
  for i in 0..
  {
    let isq = i*i;
    if isq > limit { break; }
    else if is_even(isq) { sum += isq; }
  }
  println!("loop sum = {}", sum);
// HOF way
  let sum2 =
    (0...).map(|x| x*x)
        .take_while(|&x| x < limit)
        .filter(|x| is_even(*x))
        .fold(0, |sum,x| sum+x);
  println!("hof sum = {}", sum2);
}


fn main() {
    // you can use chrono crate for date time operations.
    // chrono crate is more capable than standard library std::time module
    use chrono::prelude::*;
    use chrono::Duration;
   
    // you can retrieve current date in Utc timezone as follows
    let current_date = Utc::today();
    println!("Utc current date: {}", current_date);
   
    // you can retrieve current date in local timezone as follows
    let local_current_date = Local::today();
    println!("Local current date: {}", local_current_date);
   
    // you can retrieve current time in UTC as follows
    let current_time_utc = Utc::now();
    println!( "Utc current time: {}", current_time_utc);
   
    // you can retrieve current time in local time zone as follows
    let current_time_local = Local::now();
    println!("Local current time: {}", current_time_local);
   
    // you can add some duration to a chrono::Date
    // succ method gets succeeding date
   
    let today = Utc::today();
   
    let tomorrow = today + Duration::days(1);
    assert_eq!(today.succ(), tomorrow);
   
    // you can subtract some duration from a chrono
    // pred method gets previous date
   
    let today = Utc::today();
   
    let yesterday = today - Duration::days(1);
    assert_eq!(today.pred(), yesterday);
   
    // you can get UNIX timestamp (epoch) value for a datetime using
   
    // timestamp method of chrono::offset::TimeZone trait.
   
    // since timestamps have numeric representation, they are easy to store in db
    // and send through network. You can aldo prefer this notation in your APIs
    let dt = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 0);
    assert_eq!(dt.timestamp(), 1);
   
    //you can also get timestamp value of a datetime in milliseconds
    let dt = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 500);
    assert_eq!(dt.timestamp_millis(), 1500);
   
    //you can convert create a chrono::DateTime from a timestamp seconds
    let timestamp = 15;
   
    let datetime = Utc.timestamp(timestamp, 0);
    assert_eq!(datetime.timestamp(), 15);
   
    //you can get difference of two date times as follows
    let first = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 1, 0);
    let second = Utc.ymd(1970, 1, 1).and_hms_milli(0, 0, 2, 0);
   
    let difference: Duration = second.signed_duration_since(first);
    assert_eq!(difference, Duration::seconds(1));
   
    // you can also add and subtract duration from a DateTime struct
   
    let now = Utc::now();
   
    let yesterday_at_the_same_time = now - Duration::days(1);
    println!("Yesterday at the same time: {}", yesterday_at_the_same_time);
   
    //you can compare durations
    assert_eq! (Duration::days(1), Duration::hours(24));
   
    //you can format your DateTime struct using strftime formatting options
    let now = Utc::now();
    println!("{}", now. format( "%d.%m.%Y %H:%M"));
   
    //you can parse DateTime from a formatted string as below
    let some_time = NaiveDateTime::parse_from_str("01.10.2021 14:21", "%d.%m.%Y %H:%M").unwrap( );
    assert_eq!(some_time, NaiveDate::from_ymd(2021,10,1).and_hms(14,21,0));
}   
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 02:01, 3 January 2025

Cargo

Sample file

[package]
name = "hello_world"
version = "0.0.1"
authors = [ "Iain Wiseman iwiseman@bibble.co.nz" ]

Sample commands

 cargo new hello_world --bin
 cargo build
 cargo run

Fundamental Data Types

Primitive types

Cam declare with size of type or without

 // Integers

 let a:u8 = 123; // unsigned int 8 bits number immutable
 let a:i8 = 123; // signed int 8 bits number immutable
 let mut a:u8 = 123; // unsigned int 8 bits number mutable

 let mut c = 123456789 // 32-bit signed i32
 println!("c = {}", c);

 // Based on OS e.g.
 let z:isize = 123 // signed 64 bit if on 64 bit OS

 // Decimal
 let e:f64 = 2.5 // double-precision, 8 bytes or 64-bits

 // Char
 let x:char = 'x' // Note 4 bytes unicode

 // Boolean
 let g:bool = false; // Note 4 bytes unicode

Operators

 // Does not support -- and ++ but does support
 a -= 2;

 // Remainder can be calculated using
 a%3

 // Bitwise
 let c = 1 | 2 // | OR

 // Shift   
 let two_to_10 = 1 << 10; // 1024

 // Logical of standard e.g.
 let pi_less_4 = std::f64::consts::PI < 4.0; // true

Scope and shadowing

Curly braces keep scope

 fn test()
 {
   {
     let a = 5; 
   }
   println!("Broken {a}");
 }

Shadowing is fine though

 fn test()
 {
   let a = 5; 
   {
     let a = 10; 
     println!("10 {a}");
   }
   println!("5 {a}");
 }

Constants

 // Standard const
 const MEANING_OF_LIFE:u8 = 42;
 // Static const 
 static Z:i32 = 123;

Types

Tuples

Eezy peezy lemon squeezy

fn sum_and_product(x:i32,y:i32) -> (i32, i32)
{
 (x+y, x*y)
}

fn main()
{
  let sp = sum_and_product(3,4);
  let (a,b) = sp;
  let sp2 = sum_and_product(4,5);
   
  // combine
  let combined = (sp, sp2);
  let ((c,d), (e,f)) = combined;
}

Arrays

Array sizes cannot grow in rust

Simple

let mut a:[i32;5] = [1,2,3,4,5];
// Or 
let mut a = [1,2,3,4,5];
// Length
 a.len()
// Assignment
 a[0] = 321
// Printing
 println!("{:?}", )
// Testing
  if a == [1,2,3,4,5]
  {
  }
// Initialise
  let b = [1,10]; // 10 array initialised to 1

Multi Dimension

Here is a two dimension array

let mtx:[[f32;3];2] =
[
  [1.0, 0.0, 0.0],
  [0.0, 2.0, 0.0],
];

Slices

A slice is a non-owning pointer to a block of memory. For example

// Create a vector
let v: Vec<i32> = {0..5}.collect();

// Now create a slice (reference)
let sv: &[i32]= &v;

// We create a slice with only some elements
let sv1: &[i32]= &v[2..4];

// Printing these will produce the same result
println!("{:?}",v);
println!("{:?}",sv);

// And the range
println!("{:?}",sv1);

Strings

Basic String

let name = String::from("Iain");
let name = "Iain".to_string();

Two types, static string and string type

let s = "hello";
// Cannot do
// let h = s[0]
// You can iterate as a sequence using chars e.g.
for c in s.chars()
{
  println!("{}", c);
}

And now the mutable string in rust essentially an vector // Create a string

let mut letters = String::new();
// Add a char
let a = 'a' as u8;
letters.push(a as char);

// String to str (string slice)
let u:%str = &letters;

// Concatenation
let z = lettters + &letters

// Other examples
let mut abc = "hello world".to_string()'
abc.remove(0);
abc.push_str("!!!");
abc.replace("ello","goodbye")

Hashmap

Reminds me of my C++ and Java days. No surprises here for reference

let mut basket = HashMap::new();

basket.insert(String::from("banana"), 2);
basket.insert(String::from("pear"), 2);
basket.insert(String::from("peach"), 2);

Updating was a bit more tricky than expected. This was the copilot approach

struct TeamScores {
    goals_scored: u8,
    goals_conceded: u8,
}

fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
    // The name of the team is the key and its associated struct is the value.
    let mut scores = HashMap::new();

    for line in results.lines() {
        let mut split_iterator = line.split(',');
        // NOTE: We use `unwrap` because we didn't deal with error handling yet.
        let team_1_name = split_iterator.next().unwrap();
        let team_2_name = split_iterator.next().unwrap();
        let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap();
        let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();

        let team_1 = scores.entry(team_1_name).or_insert(TeamScores::default());
        team_1.goals_scored += team_1_score;
        team_1.goals_conceded += team_2_score;

        let team_2 = scores.entry(team_2_name).or_insert(TeamScores::default());
        team_2.goals_scored += team_2_score;
        team_2.goals_conceded += team_1_score;
    }

    scores
}

The suggestion was to use get_mut on hashmap but struggle to get this to work. The solution from Chris biscardi on youtube was this, clearly the rust team looked at this and did it better.

fn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {
...
        scores
            .entry(team_1_name)
            .and_modify(|team: &mut TeamScores| {
                team.goals_scored += team_1_score;
                team.goals_conceded += team_2_score;
            })
            .or_insert(TeamScores {
                goals_scored: team_1_score,
                goals_conceded: team_2_score,
            });

        scores
            .entry(team_2_name)
            .and_modify(|team: &mut TeamScores| {
                team.goals_scored += team_2_score;
                team.goals_conceded += team_1_score;
            })
            .or_insert(TeamScores {
                goals_scored: team_2_score,
                goals_conceded: team_1_score,
            });

Control Flow

if statement

Same as C++ except no brackets

 if temp > 30 
 {
    println!("Blah");
 }
 else if temp < 10 
 {
    println!("Blah"); 
 }
 else
 {
    println!("Blah"); 
 }

Elvis is like

  let a = if temp > 30 {"sunny"} else {"cloud"}

While and Loop

While

Same as C++ except no brackets

 while x < 1000
 {
 }

There is support for continue and break

Loop

Loop is while true

 loop
 {
    if y == 1 << 10 { break; }
 }

For Loop

A bit like kotlin loops (I think)

 for x in 1..11
 {
    println!("x = {}",x);
 }
 // You can get position in series as well
 for (pos,x) in (1..11).enumerate()
 {
    println!("x = {}, pos = {}",x, pos);
 }

Rust Principles

Ownership

Move

Move is when you assign a value to another variable. If we try and use a variable after the move we will get an error.

let v = vec![1,2,3]
let v2 = v;
println!("{:?}",v2)
println!("{:?}",v) // Error

Copy

When we copy something me make a new thing. They is not the same a let a = b, which is assignment. Copy means we duplicate the underlying data of the type. For primitives a copy is implemented by default. This is because the primitive has a know size. E.g. u32, bool etc. If you want to be able to copy a non primitive you need to add the derive macro. Note Clone must also be specified

#[derive(Copy, Clone)]
enum Direction {
    North,
    East,
    South,
    West,
}

#[derive(Copy, Clone)]
struct RoadPoint {
    direction: Direction,
    index: i32,
}

Clone

Clone is a method you can call on a struct if you want a second instance and not move the ownership. Here is an example. The struct obviously needs to implement the Copy/Clone macro. Cloning clearly increases the memory used.

let v = vec![1,2,3]
let v2 = v.cone();
println!("{:?}",v)
println!("{:?}",v2)

References

So references are like C++ references, but for rust this means you can pass the ownership during function call

main() {
    let mut s = String::from("Hello");
    change_string(&mut s);
}

fn change_string(some_string: &mut String) {
    some_string.push_str(", world!");
}

Note for returning a Reference
If we are returning a reference we must be returning a parameter as all local variables are destroyed. (Clearly Rust is not going to allow new MyMemory(6502)

Structs

Struct are the nearest thing to roughly classes in rust. You make a struct and then add implementation which are methods

General

There are 3 types of structs, name, tuple and unit structs

  • Named
  • Tuples
  • Unit

Name Struct

struct User
{
  active: bool,
  username: String,
  sign_in_count: u32
}

let user1 = User{active: true, username: String::from("Biil"),
  sign_in_count: 0};
println!("{}", user1.username);

...

fn build_user(username: String) -> User {
   User {
     username,
     active:true,
     sign_in_count: 1
   }
}

Tuple Struct

Tuple structs use the order in which declared to assign.

   struct Coordinates{i32,i32,i32};
   let coords = Coordinates{1,2,3};

Unit Struct

These are used to mark the existence of something

struct UnitStruct;
let a = UnitStruct{}

The example shown was when you are implementing a trait (interface) but the properties were not required for this type. So given a trait for Area, Square uses size but Point does not have an area as it is zero

trait AreaCalculator {
  fn calc_area(&self) => f64
}

struct Square {
  size: f64
}

struct Point;

impl AreaCalculator for Square { 
  fn calc_area(&self) -> f64 {
    self.size * self.size
  } 
} 

impl AreaCalculator for Point { 
  fn calc_area(&self) -> f64 {
    0.0
  } 
}

We can use it for error

struct DivideByZero;

fn divide(nom: f64, den: f64) -> Result<f64, DivideByZero> {
   if den != 0.0 {
       Ok(nom/den)
   } else {
       Err(DivideByZero)
   }
}

Example Structs

struct Point
{
  x: f64,
  y: f64
}

fn main()
{
  let p = Point { x: 30.0, y: 4.0 };
  println!("point is at ({},{})", p.x, p.y)
}

Methods on Structs

Methods on struct require the first argument to be self

Example Method

Add method len to struct

struct Line
{
 start: Point,
 end: Point
}

// Declare impl using the keyword impl. Not ends with no semi colon.
impl Line
{
  fn len(&self) -> f64
  {
    let dx = self.start.x - self.end.x;
    let dy = self.start.y - self.end.y;
    (dx*dx+dy*dy).sqrt()
  }
}

Changing an attribute

To change an attribute and ensure you do not break the borrowing rules we do

struct Square {
   width: u32,
   height: u32
}

impl Square
{
  fn area(&self) -> u32 {
     self.width * self.height
  }

  fn change_width(&mut self, new_width: u32) -> Self
  {
    self.width = new_width;
  }
}

...
main() {
...
   let mut sq = Square(width:5, height: 5);
   sq.change_point(10) 
}

Lifetime

What are Dangling References

The code below will not compile. This is because x goes out of scope before r. I am guessing this is what is known as a dangling reference.

fn test() {
    let r;
    {
        let x = 5;
        r = &x; // Error `x` does not live long enough
    }
    log::info!("{}",r);
}

Lifetime Annotations

Not sure which way around these are but you specify lifetime annotations on functions and structs and they imply information to the compiler on how long the parameters will live for.

Three Rules of Lifetimes

Here are the rules but we also need to understand what they apply to. Kind of chicken and egg. An example is give below which is broken because these rules are not followed.

  1. Each Parameter that is a reference gets its own lifetime parameter
  2. If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters
  3. If there are multiple input lifetime parameters, but one of them is &self or &mut self the lifetime is assigned to all output lifetime parameters

Example (Broken code)

Here is an example of code which cannot be compiled without lifetime being specified.

pub struct TestStruct {
    length: i32,
}

fn test2(x: &TestStruct, y: &TestStruct) -> &TestStruct { // Missing lifetime specifier
    if x.length > y.length {
      x
    }
    else {
      y 
    }
}

Adding Annotations

To do this we specify annotations. The extension in vscode does this for us using the quick fix. The code now looks like this

fn test2<'a>(x: &'a TestStruct, y: &'a TestStruct) -> &'a TestStruct {
    if x.length > y.length {
      x
    }
    else {
      y 
    }
}

My inference from this is that all parameters have the same lifetime.

Lifetime Annotations for Structs

Structs can also have lifetime annotations. If you specify a reference then you will need to specify a lifetime annotation. In the example below when we make the struct of type MyString we need to make sure that str1 does not go out of scope while x of type MyString exists otherwise it would refer to something no longer in scope.

// Without lifetime annotation will not compile.
// struct MyString {
//   text: &str,
// }


struct MyString<'a> {
  text: &'a str,
}

fn main() {

    let str1 = String::from("This is my String);
    let x = MyString(text: str1.as_str());
}

Static Lifetimes

We can also have lifetimes for statics.

let s: &'static str = "I live forever";

Doing this means the values are stored in the binary.

Enums

Example 1 with Method

Seems a bit C++ but... we can add a method as we do with structs. Note for this method we are returning something and with rust all locals are destroyed on return so we need to specify a lifetime.

enum Pet {dog, cat, fish}

impl Pet {
   fn what_am_i(self) -> &'static str {
      match self {
         Pet::dog => "I am a dog", 
         Pet::cat => "I am a cat",
         Pet::fish => "I am a fish",   
      }
   }
}

Example 2

enum Color {
  Red,
  Green,
  Blue
}

fn main()
{
  let c:Color = Color::Red;
  match c
  {
     Color::Red => prinln!("Color is Red");
     Color::Green => prinln!("Color is Green");
  }
}

Example 3 with Types

enum Color {
  Red,
  Green,
  Blue,
  RgbColor(u8,u8,u8) // Tuple
  CmykColor{cyan:u8, magenta:u8, yellow:u8, black:u8,} // Struct
}

fn main()
{
  let c:Color = Color::RgbColor(10,0.0);
  match c
  {
     Color::Red => prinln!("Color is Red");
     Color::Green => prinln!("Color is Green");
     Color::RgbColor(0,0,0) => prinln!("Color is Black");
     Color::RgbColor(r,g,b) => prinln!("Color is {},{},{}", r,g,b);
  }

  let d:Color = Color::CmykColor(cyan:0, magenta:0, yellow:0, black:0);
  match d
  {
     Color::Red => prinln!("Color is Red");
     Color::Green => prinln!("Color is Green");
     Color::RgbColor(0,0,0) => prinln!("Color is Black");
     Color::CmykColor(cyan:_, magenta:_, yellow:_, black:255) => prinln!("Black");
  }
}

Option<T> Enum

This enum if provided for us by rust and looks like this

enum Option<T> {
    None,
    Some(T)
}

We would choose this type when we have a case where there could be a value or not. I guess this is the equivalent of string? in Typescript where we may or may not have a value. In rust we use match to support this type.

  let some_number = Some(5); 
  let some_string = Some("a string");
  let nothing: Option<i32> = None;

Pattern Matching

Match is Exhaustive approach to pattern matching. I.E. you need to specify something for every option you are using match for. However you can include a default. I find this a great approach

Examples

Simple Match

match x
{
  0 => "zero"
  1 | 2 => "one or two"
  9...11 => "lots of"  // two dots does not include end value (exclusive)
  _ if(blahh) => "something"
  _ => "all others"
}

Here is another example.

 let country = match country_code
 {
    44 => "uk",
    46 => "sweden",
    7 => "russia"
    1...999 => "unknown" // other triple dot does include end value (inclusive)
    _ => "invalid" // invalid
 };

This just shows inclusive which is ..= unlike kotlin which I think is 3 dots

// This function returns how much icecream there is left in the fridge.
// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,
// someone eats it all, so no icecream is left (value 0). Return `None` if
// `hour_of_day` is higher than 23.
fn maybe_icecream(hour_of_day: u16) -> Option<u16> {
    match hour_of_day {
        0..22 => Some(5),
        22..=23 => Some(0),
        _ => None,
    }
}

More Complex

Stumped me when see thing for the first time prior to type script and possibly lambda. Here we define anonymous functions which match the type of the enum. Here is the enum which is used in another struct

enum Message {
    Move(Point),
    Echo(String),
    ChangeColor(u8, u8, u8),
    Quit,
    Resize { width: u64, height: u64 },
}

It has functions for each enum type.

struct State {
    width: u64,
    height: u64,
    position: Point,
    message: String,
    // RGB color composed of red, green and blue.
    color: (u8, u8, u8),
    quit: bool,
}

impl State {
    fn resize(&mut self, width: u64, height: u64) {
        self.width = width;
        self.height = height;
    }

    fn move_position(&mut self, point: Point) {
        self.position = point;
    }

    fn echo(&mut self, s: String) {
        self.message = s;
    }

    fn change_color(&mut self, red: u8, green: u8, blue: u8) {
        self.color = (red, green, blue);
    }

    fn quit(&mut self) {
        self.quit = true;
    }

    fn process(&mut self, message: Message) {
...
    }
}

At first I struggled to understand how to implement process but all you need to do is provide an ()_=> {} for each type. For Quit I completely understood but for the others was confused. Obvious once you know and I am sure copilot will do this for me

    fn process(&mut self, message: Message) {
        match message {
            Message::Move(point) => self.move_position(point),
            Message::Echo(output) => self.echo(output),
            Message::ChangeColor(red, green, blue) => self.change_color(red, green, blue),
            Message::Quit => self.quit(),
            Message::Resize { width, height } => self.resize(width, height),
        }
    }

Match on Tuples

This is an exert from [Game of Life]. We can match on tuples, and I imagine other types too. For tuples you can specify a value or compare to a value. Note the use of otherwise

    let next_cell = match (cell, live_neighbors) {
      // Rule 1: Any live cell with fewer than two live neighbours
      // dies, as if caused by underpopulation.
      (Cell::Alive, x) if x < 2 => Cell::Dead,
      // Rule 2: Any live cell with two or three live neighbours
      // lives on to the next generation.
      (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive,
      // Rule 3: Any live cell with more than three live
      // neighbours dies, as if by overpopulation.
      (Cell::Alive, x) if x > 3 => Cell::Dead,
      // Rule 4: Any dead cell with exactly three live neighbours
      // becomes a live cell, as if by reproduction.
      (Cell::Dead, 3) => Cell::Alive,
      // All other cells remain in the same state.
      (otherwise, _) => otherwise,
};

Operators and Symbols

Found in Table B-1 here [Operators and Symbols]

  1. [Range]: 1..10
  2. [RangeFrom]: 1..
  3. [RangeTo]: ..10
  4. RangeFull: ..
  5. RangeInclusive: 1..=10
  6. RangeToInclusive: ..=10

Option <T> and if let

Used to avoid null or invalid values. This was used in things where the value might be present. Maybe command line arguments where some were provide or none were provided. Lets to the classic divide by zero.

let x = 3.0
let y = 0.0 // Divide by zero

let result:Option<f64> = 
   if y != 0.0 { Some(x/y) } else { None };

// Using match
match result {
   Some(z) => println!("Goody result"),
   None => println!("No result")
}

// Using if let
if let Some(z) = result { println!("z = {}", z); }

More if let

Here is another example

let mut stack = Vec:new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
   println!("{}", top);
}

while let

The above example makes great sense but while doing rustlings the was this question

  // TODO: Make this a while-let statement. Remember that `Vec::pop()`
  // adds another layer of `Option`. You can do nested pattern matching
  // in if-let and while-let statements.
  integer = optional_integers.pop() {
    assert_eq!(integer, cursor);
    cursor -= 1;
  }

I did like the Some Some approach

   while let Some(Some(integer)) = optional_integers.pop() {
     assert_eq!(integer, cursor);
     cursor -= 1;
   }

But could not get the You can do nested pattern matching in if-let and while-let statements to look nice

   while let Some(integer) = if let Some(integer) = optional_integers.pop() {
     integer
   } else {
     None
   } {
     assert_eq!(integer, cursor);
     cursor -= 1;
   }

Just wouldn't let it lie, found a way to turn it up the right way

   while let Some(integer) = optional_integers.pop() {
     if let Some(integer) = integer {
       assert_eq!(integer, cursor);
       cursor -= 1;
     }
   }

Generics

Simple

This is very similar to C++ Templates and TypeScript Generics

struct Point<T>
{
  x: T,
  y: T
}

fn generics()
{
  let a:Point<i32> = Point {x: 0, y: 4}
}

Using Implementation

An example where we use an implementation

struct Wrapper<T> {
    value: T,
}

impl<T> Wrapper<T> {
    fn new(value: T) -> Self {
        Wrapper { value }
    }
}

Traits

Traits are similar to interfaces in java and c#

Defining a Traits

trait Animal
{
  fn create(name:&'static str);

  fn name(&self) => &'static str;

  fn talk(&self)
  {
     println!("{} cannot talk",self.name()); 
  }
}

Implement a Trait

Here we create a struct which will implement out trait. Note we do not have to implement all functions if the trait provides a default implementation

Implement a Trait for Animal

struct Human
{
   name: &'static str;
}

impl Animal for Human
{
  fn create(name:&'static str) -> Human
  {
    Human{name: name}
  }

  fn name(&self) -> &'static str
  { 
     self.name
  }
  // override default
  fn talk(&self)
  {
     println!("{} can talk",self.name()); 
  }
}

Implement a Trait for Cat

Here we implement the Animal Trait for Cat

struct Cat
{
   name: &'static str;
}

// Implement interface
impl Animal for Cat
{
  fn create(name:&'static str) -> Cat
  {
    Cat{name: name}
  }

  fn name(&self) -> &'static str
  { 
     self.name
  }
  // override default
  fn talk(&self)
  {
     println!("{} says meeow",self.name()); 
  }
}


// Usage
let h:Human = Animal::create("John");
let c:Cat = Animal::create("John");

Default Trait and Spread

For a struct we can create a default for it. We can use a typescript like spread operator (although it must be last) for override these defaults

pub struct Circle {
    color: String,
    point: Point,
    radius: u16,
}

impl Circle {
    pub fn new(color: String, point: Point, radius: u16) -> Circle {
        Circle {
            color,
            point,
            radius,
        }
    }

    pub fn default_color(point: Point, radius: u16) -> Circle {
        Circle {
            point,
            radius,
            ..Default::default()
        }
    }
}

impl Default for Circle {
    fn default() -> Self {
        Circle {
            color: String::from("black"),
            point: Point::new(0, 0),
            radius: 0,
        }
    }
}

// Default Circle
let circle = Circle::default();

// Default Black Circle
let circle = Circle::default_color(Point::new(1, 1), 1);

Traits and with Dyn and Impl

To allow any struct which implements the trait we use the dyn or impl keyword

Difference Between dyn and impl

  • dyn means dynamic dispatch. This results in a fat pointer. One pointer pointing to the data and the other pointing to the vtable.
  • impl means static dispatch. This results in a new copy of the function for each usage

Example

trait Licensed {
    fn licensing_info(&self) -> String {
        "Default license".to_string()
    }
}

struct SomeSoftware;
struct OtherSoftware;

impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}

// TODO: Fix the compiler error by only changing the signature of this function.
fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool {
    software1.licensing_info() == software2.licensing_info()
}

// Now we can do this
compare_license_types(SomeSoftware, OtherSoftware)
compare_license_types(OtherSoftware, SomeSoftware)

Why Box

When can return a dyn Trait too. We clearly do not know the size of the return value. To overcome this we put the return argument in a Box which is essentially a smart pointer in C++

fn random_animal(random_number: f64) -> Box<dyn Animal> {
    if random_number < 0.5 {
        Box::new(Sheep {})
    } else {
        Box::new(Cow {})
    }
}

Super Traits

We can make a trait to which says you must implement these traits to implement me.

trait Amphibious : WaterCapable + LandCapable + UnderWaterCapable {}

Now when you use this trait you have to implement the other 3 traits.

Provided Traits

Drop Trait

Drop trait is called automatically to free up resources but you can write your own e.g. for the example above we could write

impl Drop for Course {
  fn drop(&mut self) {
     println("Dropping")
  }
}

Clone Trait

Like the drop trait we can implement our own. Refer to the clone trait for this.

Copy Trait

We can either specify #[derive(Copy, Clone)] or implement our own. There are restrictions on this

From and Into Trait

This allow us to convert from one type to another

fn into(self) -> T
fn from(T) ->  Self
fn try_into(self) -> Result<T, Self: Error>
fn try_from(value: T) -> Result<Self, Self: Error>

Trait Bounds 1

In order to allow use of more than on trait in a function we can use the +. This example means that item must implement both traits, i.e. SomeTrait and OtherTrait

fn some_func(item: impl SomeTrait + OtherTrait) -> bool {
    item.some_function() && item.other_function()
}

Trait Bounds 2

Here is an example of doing the same thing in two ways. Because we can have anything in grade (T) we must make an implementation for std::fmt::Display. That way if we make a ReportCard with a generic which does not support Display, it will not compile

struct ReportCard<T> {
    grade: T,
    student_name: String,
    student_age: u8,
}

// Approach 1
impl<T> ReportCard<T>
where
    T: std::fmt::Display,
{
    fn print(&self) -> String {
        format!(
            "{} ({}) - achieved a grade of {}",
            &self.student_name, &self.student_age, &self.grade,
        )
    }
}

// Approach 2
impl<T: std::fmt::Display> ReportCard<T> {
    fn print(&self) -> String {
        format!(
            "{} ({}) - achieved a grade of {}",
             &self.student_name, &self.student_age, &self.grade,
        )
    }
}

Trait Bounds

The example above has two ways to achieve the same thing. If we constrain what this allowed, this is called trait bounds. Lets add a second parameter.

// This example only forces the struct to implement the trait
// fn overview(item1: &imp Overview, item2: &imp Overview) 

// But this force the struct to be of the same type
// fn overview<T: Overview>(item1: &T, item2: &T)

We can add more constraints with the + operator. Now they need the second trait.

// fn overview(item1: &imp Overview + AnotherTrait, item2: &imp Overview + AnotherTrait) 
// fn overview<T: Overview + AnotherTrait>(item1: &T, item2: &T)

Here we have an example of ensuring that the incoming parameters are constrained to be of type T

struct Pointy<T> {
    x: T,
    y: T,
}

impl <T> Add for Pointy <T>
where T: Add<Output = T>
{
    type Output = Self;

    fn add(self, other: Self) -> Self {
        Self {
            x: self.x + other.x, 
            y: self.y + other.y,
        }
    }
}

Passing Trait as Parameters

So here is an example of two structs with overview implement,one using the trait default implementation, the other its own. We can use the trait similar to a pointer to a function.

Example

trait Overview {
  fn overview(&self) -> String {
     format("This is a rust course")
  } 
}

struct Course {
  headline: String,
  author: String
}

struct AnotherCourse {
  headline: String,
  author: String
}

impl Overview for Course {
}

impl Overview for AnotherCourse {
  fn overview(&self) -> String {
     format("{}, {}", self.author, self.headline)
  } 
}

We can use the overview trait a a fn parameter with

fn call_overview(item: &imp) {
   println("Overview: {}", item.overview())
}

// OR 
fn call_overview<T: Overview>(item: &T) {
   println("Overview: {}", item.overview())
}

Passing Traits (From Youtube)

Taken from Youtube and repeated. There are two notations for passing a trait. These are the same but the first is perhaps more readable. The second is known as a trait bound.

pub fn foo (traitor: &impl SpiDevice) {

}

pub fn foo<T: SpiDevice>(traitor: &T) {

}

With the impl syntax we can make the parameter have more the one trait with a plus.

pub fn foo (traitor: &impl SpiDevice + AnotherTrait) {

}

With the second syntax if we have two parameters if allows us to make sure they both share the same trait easily as the type is only specified once

pub fn foo<T: SpiDevice>(traitor1: &T, traitor2) {

}

We can also add a second trait with this syntax too.

pub fn foo<T: SpiDevice + AnotherTrait>(traitor1: &T, traitor2) {

}

This starts to get messy to we can tidy this up with the Where Clause

pub fn foo<T, U>(traitor1: &T, traitor2: &U) -> i32
where 
    T: SpiDevice + AnotherTrait,
    U: AnotherTrait + YetAnotherTrait
        {
            42
        }

Returning Traits (From Youtube)

We can also return traits but you cannot return different types which share the same trait at this time.

pub fn foo() -> SpiDevice {
    // Must be of same type
}

Common Collections

Vectors

Same a c++

let mut a = Vec::new()
a.push(1);
a.push(2);
a.push(3);
// Print
println!("a[0] {}", a[0]);

// We can create vector with initial capacity
let mut b = Vec::<i32>::with_capacity(2);

// We can initialize using an iterator values of 0-4
let c: Vect<i32> = (0..5).collect();

// Using get returns a option
match a.get(3333)
{
...
}

// Removing, pop returns an option   
let last_elem = a.pop();

// Using the option type iterating over vector to print it
while let Some(x) = a.pop()
{
   println!("x = {}",x);
}

Binary Heap

This struct makes sure the highest is at the top. It has a peek function to allow you to peek at values.

let mut bHeap = BinaryHeap::new();
bHeap.push(1);
bHeap.push(18);
bHeap.push(20);
bHeap.push(5);
bHeap.pop();

println!("{:?}", bHeap); // 20

Maps

Not discussed

Sets

Not discussed

Error Handling

Panic

Panic happens when unhandled error occurs. This happens for instance when we access out of bounds array. We can get a backtrace by setting the environment export RUST_BACKTRACE=-1

Result Enum

The Result an enum which has two generics Result<T, E> where T is the type an E is the error. In rust we use the match to determine what to do.

let file = File::Open("Does_not_exist.mp3");
let file match file {
    Ok(file) => file,
    Err(error) => panic("Error: {:?}", error),
};

Mapping Errors

Rust likes you to make your own errors and map the ones you handle to you errors which makes sense. We make our own errors using enums

enum ParsePosNonzeroError {
    Creation(CreationError),
    ParseInt(ParseIntError),
}

Now we can provide helper function to convert from one type of error to ours

impl ParsePosNonzeroError {
    fn from_creation(err: CreationError) -> Self {
        Self::Creation(err)
    }

    // TODO: Add another error conversion function here.
    fn from_parse_int(err: ParseIntError) -> Self {
        Self::ParseInt(err)
    }
}

Now in our parse function we can map the errors in parse() to our own

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

impl PositiveNonzeroInteger {
    fn new(value: i64) -> Result<Self, CreationError> {
        match value {
            x if x < 0 => Err(CreationError::Negative),
            0 => Err(CreationError::Zero),
            x => Ok(Self(x as u64)),
        }
    }

    fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
        // TODO: change this to return an appropriate error instead of panicking
        // when `parse()` returns an error.
        let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;
        Self::new(x).map_err(ParsePosNonzeroError::from_creation)
    }
}

Testing

We specify the cfg option and use the assert library

fn sqrt(number: f64) -> Result<f64, String> {
    if number >= 0.0 {
        Ok(number.powf(0.5))
    } else {
        Err("negative floats don't have square roots".to_owned())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sqrt() -> Result<(), String> {
        let x = 4.0;
        assert_eq!(sqrt(x)?.powf(2.0), x);
        Ok(())
    }
}

We can run these with

cargo test

Closures

Closures are functions you which you can use the available scope to with that function. They look like anonymous function in typescript

Simple Example of a closure

The adder closure takes a parameter and captures existing scope at creation to produce the answer 17.

    let x: i32 = 5;
    let adder = |a| a + x;
    let b = adder(12);
    print!("b {}", b)

Mapper Function using Closure

Here is a closure which is like typescript mapper function using closures and rust

things.map((element: number) => element * 2)

And now the same thing in rust

things.iter().map(|element| element * 2).collect()

Mapper replacing a for loop using Closure

The key to do thing was to specify the type with the sum i.e. sum::<usize>() rather than sum()

fn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize {
    let mut count = 0;
    for val in map.values() {
        if *val == value {
            count += 1;
        }
    }
    count
}

fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
    let count = map
        .values()
        .into_iter()
        .map(|x| if *x == value { 1 } else { 0 })
        .sum::<usize>();
    count
}

Mapper replacing a two dimensional for loop using Closure

fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
    let mut count = 0;
    for map in collection {
        for val in map.values() {
            if *val == value {
                count += 1;
            }
        }
    }
    count
}

fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
    // Return a count of all the
    let count: usize = collection
        .iter()
        .map(|x| {
            x.values()
                .into_iter()
                .map(|y| if *y == value { 1 } else { 0 })
                .sum::<usize>()
        })
        .sum();

    count
}


More Complex Example of closure

We use the existing struct, make a copy and call them inside the to pass in objects from somewhere and use their results with the closure function

fn create_percent_complete_fn<'a>(
  &'a self,
  looper: &'a LooperDuration,
  interpolator: &'a AccelerateDecelerateInterpolator,
) -> impl Fn(u16) -> f64 + 'a {
  let looper_ref = looper;
  let interpolator_ref = interpolator;
  let closure = move |elapsed_time: u16| {
    let rate_of_change = looper_ref.get_t_with_elapsed_time(elapsed_time);
    let b = interpolator_ref.get_interpolar(rate_of_change);
    return b * 100 as f64;
  };
  return closure;
}

Traits provided for closures

These traits are provided in rust

  • Fn
  • FnOnce
  • FnMut

We need to provide some explanation for this

/*
  || drop(v)             FnOnce as we only drop once
  |args| v.contains(arg) Fn as does not modify
  |args| v.push(arg)     FnMut as it does modify 
*/

Iterators

Nothing fancy here except we have to make the iterator mut (not surprising but worth mentioning). An iterator is any type which implements the iter trait and an iterable is a type that implements into iterator.

let vec = [1, 2, 3];
let mut iter = vec.iter();
while let Some(i) = iter.next() {
    println!("i {}", i);
}

Just like Typescript we can now use closures like the map, filter functions in typescript. E.g. given a vector of times we can filter on property

items.into_iter().filter(|i|i.name == search_name).collect()

We can implement our own iterator on our struct like below

struct Range {
    start: u32,
    end: u32
 }


impl Iterator for Range {
    type Item = u32;
    fn next(&mut self) -> Option<Self::Item> {
       if self.start >= self.end {
          return None 
       }  
       let result = Some(self.start);
       self.start += 1;
       result
    }
 }

Example 1 Iterators

This iterates over the input slice, calling next() once. At this point first contains the first item in input and chars now contains the rest. We concatenate the two values.

fn capitalize_first(input: &str) -> String {
    let mut chars = input.chars();
    match chars.next() {
        None => String::new(),
        Some(first) => {
            let capital = first.to_uppercase();
            format!("{}{}", capital, chars.as_str())
        }
    }
}

Example 2 Iterators Mapping over slice

This is a good example of mapping like typescript but over a slice

fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
    words
        .iter()
        .map(|&element| capitalize_first(element))
        .collect::<Vec<_>>()
}

Pointers

This is the same a C++ with some rusty new language. The Box is a pointer type that uniquely owns a heap allocation of type

Box Type

This the basic pointer

let t = (12, "eggs");
let b = Box::new(t);
println!("b {:?}", b); // (12, "eggs")

RC Type

This allows multiple pointers to the same thing in memory. For example

let s1 = Rc::new(String::from("Pointer));
let s2 = s1.clone();
let s3 = s2.clone();

println("{},{},{}",s1,s2,s3) // Pointer, Pointer, Pointer

RefCell

A RefCell is another way to change values without needing to declare mut. It means "reference cell", and is like a Cell but uses references instead of copies, Rust refers to Interior mutability which seems to say it is a pattern where the user is allow to modify data despite the data not having a mut associated with it. The function of RefCell allow us to modify when we dereferenced.

use std::{cell::RefCell};

struct Flagger {
    is_true: RefCell<bool>,
 }

// borrow returns Ref<T>
// borrow_mut returns RefMut<t>

let flag = flagger{ is_true: RefCell::new(true)};

// let reference =  flag.is_true.borrow();
// println!("{}", reference);

let mut mut_ref = flag.is_true.borrow_mut();
*mut_ref = false;

Note if we want to use it twice, i.e. uncomment the println we have to wrap the RefCell with RC

   // In struct
   is_true: Rc<RefCell<bool>>

   // Initialize
   let flag = flagger{ is_true: Rc::new<RefCell::new(true))};

Rust Concurrency and Asynchronous Processing

This can be found Rust Concurrency and Asynchronous Processing

Concurrency

This can be found Rust Concurrency and Asynchronous Processing

Macros

Because this page was so large I moved this to Rust_macros

Functions

Functions and Arguments

No surprises

fn print_value(x: i32)
{
  println("value = {}", x);
}

Pass by reference

fn increase(x: &mut i32)
{
  *x = 1;
}

Return value, note no semicolon

fn product(x: i32, y: i32) -> i32
{
  x * y
}

Return two values, note no semicolon

fn product(x: i32) -> i32
{
  if x == 10 {
     123
  }
  
  321
}

Higher-order functions

Not sure what this is, seems like just a way to chain written functions together like lamba. Here is the given example.

fn is_even(x: u32) -> bool 
{
   x%2 == 0 
}

fn main()
{
// Method without HOF
  let limit = 500;
  let mut sum = 0;

  for i in 0..
  {
    let isq = i*i;

    if isq > limit { break; }
    else if is_even(isq) { sum += isq; }
  }

  println!("loop sum = {}", sum);

// HOF way

  let sum2 =
     (0...).map(|x| x*x)
        .take_while(|&x| x < limit)
        .filter(|x| is_even(*x))
        .fold(0, |sum,x| sum+x);

  println!("hof sum = {}", sum2);
}