Not hosted on us-east-1


Learning rust

Notes to self about learning rust


🗓️ Created: 2026-07-22 | Modified: 2026-08-11

The take aways from The book on rust that took my knowledge from point A to B as primarily a Python developer.

Chapter 2 - Programming a Guessing Game

Declaring variales

Let

https://doc.rust-lang.org/stable/book/ch02-00-guessing-game-tutorial.html#:~:text=let%20statement

let declares a variable in the local scope

variables are immutable by default


::

"The :: syntax in the ::new line indicates that new is an associated function of the String type"


Method naming

in object oriented we call a function on an object a method


Clone

In rust you can reference the same object, and do a object.clone, similar to python


String

use std::io;

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guessed: {guess}");
}
I'm learning rust, why do we instantiate a variable with `String::new();` only to append something to it afterwards, is it functional or for legibility, reply concisely.

In Rust, `String::new()` is the common start with an empty owned buffer pattern. read_line(&mut guess) needs a String you can mutate (grow) as input arrives, so you create an empty one first.

Its functional (you need an owned, mutable String to pass to read_line), and also idiomatic/legible because it makes the intent—“this will hold the users input”—clear.

Result enums

Result enum states are called variants


Rust doesn’t yet include random number functionality in its standard library

The specifier 0.8.5 is actually shorthand for ^0.8.5, which means any version that is at least 0.8.5 but below 0.9.0.

Chapter 3 - Common Programming Concepts

Looping chars

fn main() {
    for n in 1..u32::MAX {
        let c: char = match char::from_u32(n) {
            Some(j) => j,
            None => {
                // println!("Can't get number for char value {n}");
                continue;
            }
        };
        // print!(" {n} {c}");
        print!("{c}");
    }
}

Keeps trying with ints that don't have a unicode char

The rust book states: "Unicode scalar values range from U+0000 to U+D7FF and U+E000 to U+10FFFF inclusive"

Coming back to this after the end of chapter 3:

fn main() {
    for n in 1.. {
        let c: char = char::from_u32(n).expect(&format!("Can't get number for char value {n}"));
        print!("{c}");
    }
}
thread 'main' (177732) panicked at src/main.rs:6:17:
Can't get number for char value 55296
fn print(char: u32) {
    let char: char = char::from_u32(char).expect(&format!("Can't get number for char value {char}"));
    print!("{char}");
}

fn main() {
    for n in 0..0xD7FF {
        print(n);
    }
    for n in 0xE000..0x10FFFF {
        print(n);
    }

}

Finally writing some rust i must admit that i find rusts compiler errors are really friendly, the syntax is plesent and legible, and leads you to handle errors.

Assignments

Temperature converter

use std::io;

fn celsius_to_fahrenheit(celsius: f64) -> f64 {
    (celsius * 1.8) + 32.0
}

fn fahrenheit_to_celsius(fahrenheit: f64) -> f64 {
    (fahrenheit - 32.0) / 1.8
}

fn main() {
    println!("Input temperature in the format: <number><unit> e.g: 22C or 30.4F");

    let mut input = String::new();
    io::stdin()
        .read_line(&mut input)
        .expect("Failed to read line");

    let (temp, unit) = input.trim().split_at(input.len() - 2);
    let unit = unit.to_uppercase();

    let temp: f64 = temp.parse().expect(&format!("Couldn't parse number from: {temp}"));
    println!("temp: {temp}, unit: {unit}");

    let result_unit: char;
    let result: f64 = match unit.as_str() {
        "F" => {
            result_unit = 'C';
            fahrenheit_to_celsius(temp)
        }
        "C" => {
            result_unit = 'F';
            celsius_to_fahrenheit(temp)
        }
        _ => panic!("Unknown unit {unit}"),
    };
    println!("{temp}°{unit} = {result}°{result_unit}")

}

Asking an ai bot to give feedback

I'm learning rust, coming from python and some C programming, I've read the first 3 chapters of the rust book, which covers:
...
Your code is solid Rust and it mostly avoids common beginner pitfalls from the book—but there are a few correctness/robustness issues and a couple “Rust-idiomatic” improvements.

It validates me, wow, what a supprise feel_the_validation.webp

1. split_at(input.len() - 2) is brittle and can be incorrect because you compute the index from the untrimmed string length (so whitespace/newline can break the split).
2. Slicing by byte index is unsafe for general cases; indexing must land on a valid char boundary (your current approach assumes a fixed layout).
3. Parsing by last 2 characters assumes the unit is always exactly one character and located at a fixed position; inputs like 22 C, missing unit, etc., will fail or behave unexpectedly.
4. Your match works, but using result_unit + mutation in match arms is less idiomatic; returning both values directly is cleaner.
5. Using panic!/expect for invalid user input is fine for an exercise, but for real programs youd prefer returning Result and printing a friendly error.

If you want a robust fix: parse from let trimmed = input.trim(); and take the last non-whitespace character as the unit, then parse the remaining prefix as the number.

Nothing major, but let's address it to learn.

diff --git a/src/main.rs b/src/main.rs
index 9fee6af..2771e71 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -15,23 +15,17 @@ fn main() {
     io::stdin()
         .read_line(&mut input)
         .expect("Failed to read line");
+    input = input.trim().to_string();

-    let (temp, unit) = input.trim().split_at(input.len() - 2);
+    let (temp, unit) = input.split_at(input.len() - 1);
     let unit = unit.to_uppercase();

     let temp: f64 = temp.parse().expect(&format!("Couldn't parse number from: {temp}"));
     println!("temp: {temp}, unit: {unit}");

-    let result_unit: char;
-    let result: f64 = match unit.as_str() {
-        "F" => {
-            result_unit = 'C';
-            fahrenheit_to_celsius(temp)
-        }
-        "C" => {
-            result_unit = 'F';
-            celsius_to_fahrenheit(temp)
-        }
+    let (result_unit, result) = match unit.as_str() {
+        "F" => ('C', fahrenheit_to_celsius(temp)),
+        "C" => ('F', celsius_to_fahrenheit(temp)),
         _ => panic!("Unknown unit {unit}"),
     };
     println!("{temp}°{unit} = {result}°{result_unit}")

Fibonacci

fn main() {
    const iterations: usize = 20;

    let mut prev: usize = 0;
    let mut cur: usize = 1;

    for _ in 0..iterations - 1 {
        let temp: usize = cur;
        cur = cur + prev;
        println!("{prev} + {temp} = {cur}");
        prev = temp;
    }
    println!("{iterations}'th fibonacci number is: {cur}");
}
$ cargo run
   Compiling fibonacci v0.1.0 (/home/user/projects/my_misc/rust/learning/3/fibonacci)
warning: constant `iterations` should have an upper case name
 --> src/main.rs:2:11
  |
2 |     const iterations: usize = 20;
  |           ^^^^^^^^^^
  |
  = note: `#[warn(non_upper_case_globals)]` (part of `#[warn(nonstandard_style)]`) on by default
help: convert the identifier to upper case
  |
2 -     const iterations: usize = 20;
2 +     const ITERATIONS: usize = 20;
  |

$ cargo fix --bin "fibonacci" -p fibonacci

warning: `fibonacci` (bin "fibonacci") generated 1 warning (run `cargo fix --bin "fibonacci" -p fibonacci` to apply 1 suggestion)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.28s
     Running `target/debug/fibonacci`

error: the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, or commit the changes to these files:

  * .gitignore (dirty)
  * Cargo.lock (dirty)
  * Cargo.toml (dirty)
  * src/ (dirty)

$ cargo fix --bin "fibonacci" -p fibonacci --allow-dirty

    Checking fibonacci v0.1.0 (/home/user/projects/my_misc/rust/learning/3/fibonacci)
       Fixed src/main.rs (1 fix)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s


Again, really nice and native tooling

fn main() {
    const ITERATIONS: usize = 20;

    let mut prev: usize = 0;
    let mut cur: usize = 1;

    for _ in 0..ITERATIONS - 1 {
        let temp: usize = cur;
        cur = cur + prev;
        println!("{prev} + {temp} = {cur}");
        prev = temp;
    }
    println!("{ITERATIONS}'th fibonacci number is: {cur}");
}
$ cargo run
0 + 1 = 1
1 + 1 = 2
1 + 2 = 3
2 + 3 = 5
3 + 5 = 8
5 + 8 = 13
8 + 13 = 21
13 + 21 = 34
21 + 34 = 55
34 + 55 = 89
55 + 89 = 144
89 + 144 = 233
144 + 233 = 377
233 + 377 = 610
377 + 610 = 987
610 + 987 = 1597
987 + 1597 = 2584
1597 + 2584 = 4181
2584 + 4181 = 6765
20'th fibonacci number is: 6765

Correct

Currious, if i keep running it, i outgrow the instruction set of usize on x86_64, 19.740.274.219.868.223.167 > 2^64 (18,446,744,073,709,551,616)

89 1779979416004714189 + 2880067194370816120 = 4660046610375530309
90 2880067194370816120 + 4660046610375530309 = 7540113804746346429
91 4660046610375530309 + 7540113804746346429 = 12200160415121876738

thread 'main' (266042) panicked at src/main.rs:9:15:
attempt to add with overflow
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
>>> 7540113804746346429 + 12200160415121876738
19740274219868223167

Python don't care, will have to keep that in mind. Useful for efficiency and embedded systems.

Twelve days of christmas

const DAYS: [&str; 12] = ["first", "second", "third", "fourth", "fifth", "sixth",
                          "seventh","eighth", "ninth", "tenth", "eleventh", "twelfth"];

const THINGS: [&str; 12] = [
    "A partridge in a pear tree",
    "Two turtle doves and",
    "Three french hens",
    "Four calling birds",
    "Five golden rings",
    "Six geese a-laying",
    "Seven swans a-swimming",
    "Eight maids a-milking",
    "Nine ladies dancing",
    "Ten lords a-leaping",
    "Eleven pipers piping",
    "Twelve drummers drumming"
];

fn main() {
    for i in 0..12 {
        println!("On the {} day of Christmas, my true love sent to me", DAYS[i]);
        for j in (0..i + 1).rev() {
            println!("{}", THINGS[j]);
        }
        println!();
    }
}
On the first day of Christmas, my true love sent to me
A partridge in a pear tree

On the second day of Christmas, my true love sent to me
Two turtle doves and
A partridge in a pear tree

On the third day of Christmas, my true love sent to me
Three french hens
Two turtle doves and
A partridge in a pear tree

On the fourth day of Christmas, my true love sent to me
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree

On the fifth day of Christmas, my true love sent to me
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree

On the sixth day of Christmas, my true love sent to me
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree

On the seventh day of Christmas, my true love sent to me
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree

On the eighth day of Christmas, my true love sent to me
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree

On the ninth day of Christmas, my true love sent to me
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree

On the tenth day of Christmas, my true love sent to me
Ten lords a-leaping
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree

On the eleventh day of Christmas, my true love sent to me
Eleven pipers piping
Ten lords a-leaping
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree

On the twelfth day of Christmas, my true love sent to me
Twelve drummers drumming
Eleven pipers piping
Ten lords a-leaping
Nine ladies dancing
Eight maids a-milking
Seven swans a-swimming
Six geese a-laying
Five golden rings
Four calling birds
Three french hens
Two turtle doves and
A partridge in a pear tree

Again super error message for newcomers with helpful suggestion on how to remedy the issue.

   Compiling twelve_days_of_christmas v0.1.0 (/home/user/projects/my_misc/rust/learning/3/twelve_days_of_christmas)
error: missing type for `const` item
 --> src/main.rs:1:12
  |
1 | const DAYS: = ["first", "second", "third", "fourth", "fifth", "sixth",
  |            ^ help: provide a type for the constant: `[&str; 12]`

error: could not compile `twelve_days_of_christmas` (bin "twelve_days_of_christmas") due to 1 previous error

Chapter 4 - Understanding Ownership

context: free memory "If we forget, we’ll waste memory. If we do it too early, we’ll have an invalid variable. If we do it twice, that’s a bug too. We need to pair exactly one allocate with exactly one free. Rust takes a different path"

"Rust will never automatically create “deep” copies of your data. Therefore, any automatic copying can be assumed to be inexpensive in terms of runtime performance."

"When a variable goes out of scope, Rust calls a special function for us. This function is called drop, and it’s where the author of String can put the code to return the memory"

"When you see a call to clone, you know that some arbitrary code is being executed and that code may be expensive. It’s a visual indicator that something different is going on."

Drop and Copy traits are mutually exclusive

"as a general rule, any group of simple scalar values can implement Copy, and nothing that requires allocation or is some form of resource can implement Copy."

Examples:

Functions can either take ownership, or they can borrow, specifying whether they have permission to mutate the value.

You can't borrow immutably and mutably, pick your intent

The compiler is smart, it knows when a variable isn't used anymore

    let mut s = String::from("hello");

    let r1 = &s; // no problem
    let r2 = &s; // no problem
    let r3 = &mut s; // BIG PROBLEM

    println!("{r1}, {r2}, and {r3}");
    let mut s = String::from("hello");

    let r1 = &s; // no problem
    let r2 = &s; // no problem
    println!("{r1} and {r2}");
    // Variables r1 and r2 will not be used after this point.

    let r3 = &mut s; // no problem
    println!("{r3}");

"In Rust, by contrast, the compiler guarantees that references will never be dangling references"

We cannot have a pointer to something invalid, this is handled through scope {} Perhaps we'd define the pointer first, and pass it as an arg?

4.3 - The Slice Type

Itterating a strings chars we'd call string_var.as_bytes();

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

fn main() {
    let mut s = String::from("hello world");

    let word = first_word(&s);

    s.clear(); // error!

    println!("the first word is: {word}");
}

This example from the book

Throws this error:

   Compiling immutable_test v0.1.0 (/home/user/projects/my_misc/rust/learning/4/immutable_test)
error[E0499]: cannot borrow `s` as mutable more than once at a time
  --> src/main.rs:18:5
   |
16 |     let word = first_word(&mut s);
   |                           ------ first mutable borrow occurs here
17 |
18 |     s.clear(); // error!
   |     ^ second mutable borrow occurs here
19 |
20 |     println!("the first word is: {word}");
   |                                   ---- first borrow later used here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `immutable_test` (bin "immutable_test") due to 1 previous error

and changing it to a mutable reference doesn't eleviate the problem either Neither does giving the first word function ownership.

Wont it cause us problems that we can't have multiple references to a var? Will it not take extra care to move around ownership and only having one reference or type of reference? What are the rules, how is it used?


A more experienced Rustacean would write the signature shown in Listing 4-9 instead because it allows us to use the same function on both &String values and &str values.

This must be because &str is a more basic type, &String has more features

"The str type, also called a ‘string slice’, is the most primitive string type"

"A &str is made up of two components: a pointer to some bytes, and a length"


You can copy fields from another instance of an object, very peruclear, wouldn't this easily lead to errors? Copying something inadvertently, I'm sure it makes sense in it's prober use case.

source

    let user2 = User {
        email: String::from("another@example.com"),
        ..user1
    };

There are two types of structs: regualar and tuple

struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

struct Color(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
}

Comments


(Will await approval before becoming public)

SELECT id, nickname, comment, page_url, visitor_url, (CASE WHEN show_contact THEN contact ELSE NULL END) as contact_info, created_at
FROM comment WHERE approved AND public AND page_url = '/misc/Learning rust' ORDER BY created_at DESC;
*No comments*