Machine whisperer

Getting those brain gears turning with some Rust

Let's talk a little about language learning. Well, not about learning the weird languages that humanoids use among themselves, but rather about learning programming languages.

I've been checking out Rust for a while now, so when I read on a game developer blog that it was written in Rust, I started looking into it again. I read about game engines for Rust and tried out some sample code, but I'm aware of my limitations (as a developer, but even more so as a graphic designer), I wouldn't start developing a game, especially in a language I don't even know well.

So I went into more familiar waters and looked at what web frameworks were out there, and got quite a long way in reading the documentation of one of the more sympathetic ones when I started having trouble fully understanding the example codes. Maybe going back to the basics wouldn't hurt.

Nowadays, the Internet is full of online courses and videos, but I'm leaning towards the more traditional means of learning, so I went with the official Rust book. I got about halfway through when I was getting really bored of reading and not programming.

I also found this "exercise book" like collection, where most of the code is already written, you just have to get it to compile or the tests to turn green. I think the basic idea is very good, I have come across something like this for other languages in the past.

The only problem is that I tend to switch from learning mode to problem-solving mode for these kinds of tasks, which is great for getting through this "game" quickly, but not really helpful for deeper understanding.

I did manage to slow down in a few places, where I either couldn't solve the task after several attempts or couldn't even figure out where start, so I needed to read the documentation a bit. The first solutions often didn't turn out great, obviously, I don't know the tools provided by the language, so I work with what I have (or what I learned a few exercises ago). For example, my first solution for the hashmaps/hashmaps3.rs level turned out something like this:

match scores.get_mut(&team_1_name) {
  Option::Some(team) => {
    team.goals_scored += team_1_score;
    team.goals_conceded += team_2_score;
  }
  Option::None => {
    scores.insert(
      team_1_name.clone(),
      Team { goals_scored: team_1_score, goals_conceded: team_2_score }
    );
  }
}
match scores.get_mut(&team_2_name) {
  Option::Some(team) => {
    team.goals_scored += team_2_score;
    team.goals_conceded += team_1_score;
  }
  Option::None => {
    scores.insert(
      team_2_name.clone(),
      Team { goals_scored: team_2_score, goals_conceded: team_1_score }
    );
  }
}

Then I found out about the entry function of the HashMap and the or_insert function of the Entry, which greatly simplified the above code:

let team = scores.entry(team_1_name)
                 .or_insert(Team { goals_scored: 0, goals_conceded: 0 });
team.goals_scored += team_1_score;
team.goals_conceded += team_2_score;

let team = scores.entry(team_2_name)
                 .or_insert(Team { goals_scored: 0, goals_conceded: 0 });
team.goals_scored += team_2_score;
team.goals_conceded += team_1_score;

Later, the Team got some extra functions...

impl Team {
  fn new() -> Self { Team { goals_scored: 0, goals_conceded: 0 } }
  fn add(&mut self, scored: u8, conceded: u8) {
    self.goals_scored += scored;
    self.goals_conceded += conceded;
  }
}

...and this was my final solution:

scores.entry(team_1_name)
      .or_insert(Team::new())
      .add(team_1_score, team_2_score);
scores.entry(team_2_name)
      .or_insert(Team::new())
      .add(team_2_score, team_1_score);

After I had finished with the Rustlings levels, I had to look for a new task. A web development project still felt like too much to handle. Back when I was learning Python, I used to solve Project Euler problems for practice. I went on with a similar idea now as well.

I (also) started the Advent of Code 2023 last December and although I didn't get very far, I got solutions for the first few days in PHP (I like mixing things up) that I can reimagine in Rust.

Existing solutions are not a prerequisite for the Advent of Code exercises to be useful for some programming practice, but I personally don't like having two distractions at once: one is that I don't know how to solve the exercise yet, and the other is that I don't know Rust. That usually just leads to frustration and makes you lose interest in the whole thing.

Obviously, there are a lot of Advent of Code solutions on the Internet, but then you spend time trying to understand other people's code, so it may be better to go with your own solution.

Below is the solution for the first half of the first day, I think it turned out quite Rust-like:

use std::fs;

fn main() {
  let sum: u32 = fs::read_to_string("data/input_1.txt").unwrap()
    .lines()
    .map(calibration_value)
    .sum();

  println!("{:?}", sum);
}

fn calibration_value(line: &str) -> u32 {
  let numbers: Vec<u32> = line.chars()
    .filter_map(|c| c.to_digit(10))
    .collect();

  numbers.first().unwrap() * 10 + numbers.last().unwrap()
}

It's worth comparing it to my PHP solution, which is... how shall I put it... a bit less elegant (but at the time the goal was to solve the problem, not to write beautiful idiomatic PHP code):

<?php

$sum = 0;
foreach (file('input_1.txt') as $line) {
  $data = trim($line);
  preg_match('/^.*?(?P<digit>[0-9])/', $data, $m);
  $firstDigit = $m['digit'];
  preg_match('/.*(?P<digit>[0-9]).*?$/', $data, $m);
  $lastDigit = $m['digit'];

  $sum += intval($firstDigit . $lastDigit);
}

print("$sum\n");

Another good direction for practicing a new language (and its test environment) might be code Katas. Especially those that you have already solved in other languages so that solving the problem no longer causes any friction. But the Kata exercises are generally not that difficult anyway, so you can start without any prior preparation.

Well, that's how my days went with Rust. The next step will probably be to go back to the book for a bit to get a better understanding of some Rust-specific things (such as ownership/borrowing) and continue doing the small tasks alongside. How do you approach learning a new programming language?

Ez a bejegyzés magyar nyelven is elérhető: Gépekkel suttogó

Have a comment?

Send an email to the blog at deadlime dot hu address.

Want to subscribe?

We have a good old fashioned RSS feed if you're into that.