notes by alifeee profile picture rss

return to notes / blog / website / weeknotes / linktree

here I may post some short, text-only notes, mostly about programming. source code.

tags: all (43), scripting (13), linux (5), bash (4), geojson (4), obsidian (4), android (3), github (3), html (3), jq (3) ............ see all (+53)

viewing a single note

testing Rust for the first time by making an identicon # source

tags: rust, identicons • 8802 'words', 2641 secs @ 200wpm

I was screwing around on YouTube, and ended up watching a few videos about Rust. Actually, these ones: the first, leading to the second, leading to the third.

These videos are all by noboilerplate, and I got only 1:08 minutes into the third video before I decided to try out Rust myself.

For a long time I've been meaning to make an identicon (think: default pixelated profile picture for GitHub/etc) using Lua, after seeing a friend's identicon implementations in several language. I think, as they do, that making an identicon generator is a very fun and contained way to start experimenting with a new language - you get involved with random numbers, arrays, string formatting, loops, and maybe more.

Anyway, I still haven't made one in Lua, but I did make these three in Rust.

Installing Rust

Installing Rust was super easy, I just used the command from https://rustup.rs/.

Installing VSCodium extensions

Well, first I installed using sudo apt install cargo, but then the VSCodium extension I installed (Rust) suggested I should use rustup, so I uninstalled cargo and used rustup.

Then, I also found out that the VSCodium extension was deprecated in favour of the rust-analyzer extension, so I installed that one instead. I also installed CodeLLDB to allow debugging.

Running Rust

After installing Cargo, I ran cargo and it complained about a missing Cargo.toml, so I guessed I could run…

cargo init

…to create this, and it worked! Neat. It also showed a nice link to the documentation for Cargo.toml. I still haven't opened the Cargo.toml file. Anyway, cargo init also created a "hello world" script:

fn main() {
    println!("Hello, world!");
}

…which I could run with cargo run

$ cargo run
Hello, world!

At this point, I got stuck in trying to make the above identicons. I (naturally) came across a few stumbling blocks, but the errors that the compiler provides were quite nice, so I got along OK.

Here's the final code I ended up with (feel free to tell me that several sections are "bad" or "not Rust-y")

use rand::prelude::*;

const WIDTH: usize = 15;
const HEIGHT: usize = 15;
const SQUARE_SIZE: usize = 50;
const SVG_WIDTH: usize = WIDTH * SQUARE_SIZE;
const SVG_HEIGHT: usize = HEIGHT * SQUARE_SIZE;

fn main() {
    let mut rng = rand::rng();

    // generate one half of the identicon
    // let mut arr: [[bool; 0]; 0] = [];
    let mut arr: Vec<Vec<bool>> = vec![];
    for r in 0..HEIGHT {
        let empty_arr: Vec<bool> = vec![];
        arr.push(empty_arr);
        for _c in 0..((WIDTH + 1) / 2) {
            let random_val = rng.random_bool(0.5);
            arr[r].push(random_val);
        }
    }

    // print the SVG
    println!(
        "<svg version='1.1'
     viewbox='0 0 {} {}'
     xmlns='http://www.w3.org/2000/svg'>",
        SVG_WIDTH, SVG_HEIGHT
    );
    println!(
        "<rect width='{}' height='{}' fill='black' />",
        SVG_WIDTH, SVG_HEIGHT
    );
    for r in 0..arr.len() {
        let arr_first = arr.first();
        let mut cols = 0;
        if let Some(arr_first) = arr_first {
            cols = arr_first.len();
        }
        for c in 0..cols {
            let xleft = c * SQUARE_SIZE;
            let xright = SVG_WIDTH - xleft - SQUARE_SIZE;
            let y = r * SQUARE_SIZE;

            let filled = arr[r][c];
            let mut colour = "none";
            if filled {
                colour = "red";
            }

            println!(
                "<rect width='50' height='50' fill='{}' x='{}' y='{}' />",
                colour, xleft, y
            );
            println!(
                "<rect width='50' height='50' fill='{}' x='{}' y='{}' />",
                colour, xright, y
            );
        }
    }
    println!(r#"</svg>"#);
}

Sticking points

Two things that I got a bit stuck with were:

Not declaring loads of variables

I wasn't sure how to do a lot of things "in-line", and ended up declaring lots of variables, making the code quite verbose. For example, to push an empty vector to another vector I ended up doing (above) this…

let empty_arr: Vec<bool> = vec![];
arr.push(empty_arr);

…which I'm sure could be done in one line somehow. I don't know how.

Finding the length of an Option

To get the length of an embedded Vec (vector), I wanted to run arr.first().len() in some way, but arr.first() is either a vector or None (i.e., an optional/Option). I wanted to do something like:

if arr.first().is_none() {
  let cols = 0;
} else {
  let cols = arr.first().len();
}

…assuming that the compiler would realise that in the else section, arr.first() was not None, but it didn't. I don't know enough to figure out a way of doing this.

The End

It was quite fun using Rust for the first time.

Identicons are a lovely first project.

Perhaps I'll touch Rust again. Perhaps I won't.

back to top back to main page