Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions resources/blog-styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@
white-space: nowrap;
}

.blog-page--unhighlighted-block pre:not(.pygments-highlighter pre) {
/* Multi-line blocks */
background-color: #f8f8f8;

code {
white-space: pre;
}
}

.blog-page--has-toc.content-wrapper {
width: 100%;
display: grid;
Expand Down
2 changes: 1 addition & 1 deletion resources/default-styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
width: 80%;
margin-bottom: calc( var(--footer-elem-height) + 10px );

p, li, blockquote {
p, li, blockquote, summary {
font-size: 18px;
}
}
Expand Down
3 changes: 3 additions & 0 deletions sitemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,7 @@
<url>
<loc>https://scherzer.dev/Blog/20250912-hello-world-rust</loc>
</url>
<url>
<loc>https://scherzer.dev/Blog/20250919-rust-euler</loc>
</url>
</urlset>
270 changes: 270 additions & 0 deletions src/Blog/posts/20250919-rust-euler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
---
extensions:
footnotes: true
pygments: true
extra-classes:
- blog-page--unhighlighted-block
title: Project Euler in Rust
---

# Project Euler in Rust

After writing the obligatory "Hello, World!", I wanted to work on something
more substantial in Rust. When I learn a new programming language, my go-to
source of coding problems to try and solve in the new language is
[Project Euler][proj-euler], which features hundreds of coding challenges of
various difficulty levels.

With Rust, I didn't try to solve any new problems, but rather focused on
problems that I had already solved in other languages. That way, I could be
focused on the specifics of working in Rust rather than generally figuring out
how to solve the problem.

## Starter Problems

I kicked things off with [Problem #1][proj-euler-prob-1], which asks for the
sum of all natural numbers below 1000 that are multiples of 3 or 5 (or both).
That problem is simple enough that I didn't refer back to any of my previous
solutions - my program[^1] was as simple as:

```rust
fn main() {
let mut sum = 0;
for iii in 1..1000 {
if (iii % 3 == 0) || (iii % 5 == 0) {
sum += iii;
}
}
println!("Sum: {}", sum);
}
```

Working on this problem brought me to my first three major observations about
how Rust differs from other languages that I have used in the past:

1. Rust variables are immutable by default.

In other languages, immutability for local variables needs to be declared
explicitly (C, JavaScript) or is not possible (PHP). This will likely take some
getting used to, but I like it!

2. The Rust compiler has helpful error messages.

Without the `mut` keyword, trying to compile the program would lead to:

<details>

<summary>Expand for errors</summary>

```
error[E0384]: cannot assign twice to immutable variable `sum`
--> problem1.rs:5:4
|
2 | let sum = 0;
| --- first assignment to `sum`
...
5 | sum += iii;
| ^^^^^^^^^^ cannot assign twice to immutable variable
|
help: consider making this binding mutable
|
2 | let mut sum = 0;
| +++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0384`.
```
</details>

Not only does the compiler explain what went wrong (assigning twice to an
immutable variable), it also:

* highlights were the assignments occur,
* suggests how to fix them,
* and offers a built-in tool for exploring the error code further

That built-in tool explains that by default, variables in Rust are immutable.
Compared to the compiler errors that I am used to from C and C++, Rust is a
refreshing change of pace.

3. The Rust compiler warns about style issues too.

I'm used to the conditions for `if` statements needing to be surrounded with
`()`, i.e. `if ((iii % 3 == 0) || (iii % 5 == 0)) {`, but with Rust, those are
unnecessary. Not only that, but the compiler will warn that they are unused, and
suggest removing them. I appreciate the compiler pointing this out, though I'm
not sure how I feel about the compiler having style preferences.

[Problem #2][proj-euler-prob-2], which asks for the sum of all even terms in
the Fibonacci sequence that are less than four million, was also pretty simple.
However, one of the compiler warnings that I got introduced me to a new Rust
feature: [`loop`][rust-loop], which is used for infinite loops. Other languages
that I have used lack a dedicated mechanism for infinite loops, and so I was
used to something like `while (true)`, but the compiler gave me a helpful
nudge via a warning.

## Sudoku Solver

Having gotten a hang of the basics, I figured it wouldn't hurt to just dive in,
and so I jumped to [Problem #96][proj-euler-prob-96]. Briefly, that problem
entails reading in 50 different sudoku grids, solving them, and then for each
grid, consider the first three digits of the top row as a three digit number,
and sum those three digit numbers across the 50 sudoku grids.

To focus on learning Rust rather than re-solving the problem, I started off by
basically copying my C++ implementation of the problem and then working to adapt
it to Rust. I'm not going to discuss every difference between the languages, but
there were a few places where Rust was remarkably different from other
languages that I have used in the past that I think are worth mentioning for
anyone considering taking up Rust.

The first major difference was a big pain point in my conversion of the C++
code to Rust that required re-engineering my approach:

4. Rust prohibits having multiple mutable references to the same value.

Sudoku digits cannot repeat within the same row, column, or box of the grid.
To help with managing the nine rows, nine columns, and nine boxes, in C++ I had
a `DigitCollection` class that would hold pointers to the nine different
`Digit`s of the grid in a row/column/box. The `DigitCollection` could also
modify the values of the grid `Digit`s - if there was only one `Digit` that
could hold the value `1`, for example, then that `Digit` would be updated to be
a `1`. Then, the other two collections that the `Digit` was in would propagate
the change and remove the `1` option from the other `Digit`s.

In Rust, such an approach was not going to work. I tried various workarounds
using Rust's standard library, like the [`std::cell::RefCell`][rust-refcell]
struct, but I wasn't able to get anything to work. As a result, I instead
refactored the way `DigitCollection` worked: instead of holding a reference to
the various `Digit` instances, they would hold the details of the *locations*
(row and column) of each `Digit` in the collection. Then, any time that the
`DigitCollection` wanted to do anything, it would fetch a reference to the
needed `Digit`. Those references were short-lived and scoped to their usage, so
there would not be multiple references to the same `Digit` at once as long as
only one `DigitCollection` was doing work at a time.

At that point, I realized that the setup of the `DigitCollection` struct meant
that it was no longer tied to a specific grid. The `DigitCollection` only held
details of the *locations* of `Digit`s in the grid, not the `Digit`s themselves.
Thus, I should be able to allocate the 27 `DigitCollection` instances once at
the start (nine rows, nine columns, nine boxes) and just reuse those rather than
allocating 27 instances for each of the 50 grids. However, I quickly ran into
another limitation of Rust:

5. Rust global variables have restrictions on their initialization.

I was initializing the nine `DigitCollection` instances corresponding to the
rows of the grid with

```rust
let row_sets: [DigitCollection; 9] = core::array::from_fn(
|row|
DigitCollection {
locations: core::array::from_fn(|col| DigitLoc{ row: row, col: col } ),
}
);
```

Each of the nine rows was a `DigitCollection`, where the locations were
initialized with a callback mapping the column index to a new `DigitLoc`
struct, which just holds the row and column information together.[^2]

Unfortunately, while the snippet above worked fine within a function, doing it
for a global variable (which needs to be declared with either `const` or
`static` rather than `let`) did not work. Using either `static` or `const`
results in error E0015 complaining that the `std::array::from_fn` function is
non-const.

The final big difference that I encountered was not an issue for the first few
grids, so I did not realize the impact right away:

6. Rust does not have exceptions that can be thrown and caught.

At various points in the solving process, the solver might conclude that the
grid is impossible to solve - for example, a `Digit` might not have any possible
values, or a value might not be an option in any `Digit` in a collection. At
that point, the C++ code would throw an exception; when converting to Rust, I
started by replacing those exceptions with Rust's [`panic!` macro][rust-panic].

However, some exceptions in the C++ version were meant to be caught and handled,
rather than terminating the entire program. Specifically, after implementing a
few basic solution stategies (e.g. checking if a `Digit` could only hold a
single value, or if a value could only go in one `Digit` in a `DigitCollection`)
my approach was rather inelegant: bifurcation. I would pick one `Digit` with
multiple options, set it to be one of those options, and then try to solve the
new grid. If solving succeeded, then the "guess" was correct and the solution
from the new grid was used. If solving failed, then the "guess" was incorrect
and that option could be removed from the `Digit` in question.

To handle the "if solving failed" case, the C++ code would just catch an
exception; in Rust, the `panic!` macro aborts the entire program. I needed to
convert some of the `panic!` calls to instead use the [`Result`][rust-result]
type, which holds either a success value, or an error message. After that
migration, the program was able to compile, run, and produce the right answer!

While I've spent a while on ways that Rust's differences were frustrating or
unexpected, there was also one Rust feature that the other languages I use most
often (C, PHP,[^3] and JavaScript) lack:

7. Rust has pattern matching!!!

I've used pattern matching in some other languages ([ML][ml-lang] comes to mind)
and it makes things very nice and compact. For example, part of the switch from
exceptions to a `Result` resulted in the code:

```rust
match self.check_only_options() {
Err(_) => return false,
Ok(v) => made_change = v,
};
```

If the `check_only_options()` method (which checks each `DigitCollection` to see
if there is a value that can only go into one `Digit`) returns an error,
indicating that the solving is impossible (due to bifurcation), the solving
just returns false.

## Final Thoughts

Rust is definitely a language I want to keep using. I plan to continue playing
around with Rust for Project Euler problems, and I might try to contribute
[more patches to mago][blog-mago]. While Rust will take some adjusting to,
especially the limits on mutable references, I look forward to adding a new
language to my repertoire.

I also need to get familiar with Rust's package system for installing libraries,
[crates][rust-crates]. These seem to be a built-in part of the core language,
unlike [PHP's Composer packages][php-composer] or
[JavaScript's NPM packages][js-npm], where the package management tools are
provided by third parties.

[^1]: Normally, publishing solutions to the Project Euler problems is
discouraged, but for the first 100 problems sharing solutions is allowed.

[^2]: In Sudoku, the cells are labeled as row one (top) through row nine
(bottom) and column one (left) through column nine (right). However, in Rust and
most other programming languages that I have used, arrays are indexed starting
from `0`. Thus, the "location" of a `Digit`, indicating where it is in the
two-dimensional array of `Digits` in the grid, has a row and a column that are
in the range 0-8 rather than 1-9.

[^3]: PHP might be getting pattern matching at some point, there is
[an RFC][php-pattern-rfc] currently in draft to add that feature, but the RFC
has been in draft since 2020 so it could still be a while before it gets
discussed, accepted, and implemented, if it ever does.

[proj-euler]: https://projecteuler.net/
[proj-euler-prob-1]: https://projecteuler.net/problem=1
[proj-euler-prob-2]: https://projecteuler.net/problem=2
[rust-loop]: https://doc.rust-lang.org/rust-by-example/flow_control/loop.html
[proj-euler-prob-96]: https://projecteuler.net/problem=96
[rust-refcell]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
[rust-panic]: https://doc.rust-lang.org/std/macro.panic.html
[rust-result]: https://doc.rust-lang.org/std/result/
[php-pattern-rfc]: https://wiki.php.net/rfc/pattern-matching
[ml-lang]: https://en.wikipedia.org/wiki/ML_(programming_language)
[blog-mago]: ./20250906-mago-rust
[rust-crates]: https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html
[php-composer]: https://getcomposer.org/doc/00-intro.md
[js-npm]: https://docs.npmjs.com/about-npm
7 changes: 5 additions & 2 deletions tests/data/Home.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
Computer Science and Political Science and graduating <em>magna cum laude</em> in 2024 with a Bachelor of Science degree. As part of my work in Computer
Science, I chose to write an honors thesis in my senior year, see <a href="./Thesis">here</a> for details. I continued at Tufts for graduate school, graduating in 2025 with
a Master of Science in Computer Science degree.</p><p>See the links in the navigation bar above for more information about my
experience.</p><h2 class="subsection-header">Contact</h2><ul><li><a href="https://www.linkedin.com/in/daniel-scherzer-520539263/" target="_blank" class="external-link">LinkedIn</a></li><li>Email: <code>daniel.e.scherzer@gmail.com</code></li></ul><h2 class="subsection-header">Blog</h2><p>I also have a blog. You can see a full index of my posts <a href="/Blog">here</a>. My latest blog post is: </p><div class="blog-preview"><h3>&quot;Hello, World!&quot; in Rust</h3><span class="blog-preview-date">Friday, 12 September 2025</span><p>Yesterday, I started to teach myself how to program in Rust. As always, the first
step was a &quot;Hello, World!&quot; program. <a href="/Blog/20250912-hello-world-rust">Continue reading...</a></p></div></div><div class="des-footer"><div class="des-footer--content">Content is © 2025 Daniel Scherzer</div></div></body></html>
experience.</p><h2 class="subsection-header">Contact</h2><ul><li><a href="https://www.linkedin.com/in/daniel-scherzer-520539263/" target="_blank" class="external-link">LinkedIn</a></li><li>Email: <code>daniel.e.scherzer@gmail.com</code></li></ul><h2 class="subsection-header">Blog</h2><p>I also have a blog. You can see a full index of my posts <a href="/Blog">here</a>. My latest blog post is: </p><div class="blog-preview"><h3>Project Euler in Rust</h3><span class="blog-preview-date">Friday, 19 September 2025</span><p>After writing the obligatory &quot;Hello, World!&quot;, I wanted to work on something
more substantial in Rust. When I learn a new programming language, my go-to
source of coding problems to try and solve in the new language is
<a rel="noopener noreferrer" target="_blank" class="external-link" href="https://projecteuler.net/">Project Euler</a>, which features hundreds of coding challenges of
various difficulty levels. <a href="/Blog/20250919-rust-euler">Continue reading...</a></p></div></div><div class="des-footer"><div class="des-footer--content">Content is © 2025 Daniel Scherzer</div></div></body></html>
6 changes: 5 additions & 1 deletion tests/data/blog-index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<!DOCTYPE html>
<html><head><link rel="icon" href="data:,"><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1"><link rel="stylesheet" type="text/css" href="/resources/default-styles.css"><title>Blog index</title><link rel="stylesheet" type="text/css" href="/resources/blog-styles.css"></head><body><div class="des-navbar"><a href="/Home">Home</a><a href="/files/Resume.pdf">Résumé</a><a href="/OpenSource">Open Source</a><a href="/Work">Work</a><strong class="des-strong-page-link">Blog</strong></div><div class="content-wrapper"><h1>Blog index</h1><div class="blog-preview"><h2>&quot;Hello, World!&quot; in Rust</h2><span class="blog-preview-date">Friday, 12 September 2025</span><p>Yesterday, I started to teach myself how to program in Rust. As always, the first
<html><head><link rel="icon" href="data:,"><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1"><link rel="stylesheet" type="text/css" href="/resources/default-styles.css"><title>Blog index</title><link rel="stylesheet" type="text/css" href="/resources/blog-styles.css"></head><body><div class="des-navbar"><a href="/Home">Home</a><a href="/files/Resume.pdf">Résumé</a><a href="/OpenSource">Open Source</a><a href="/Work">Work</a><strong class="des-strong-page-link">Blog</strong></div><div class="content-wrapper"><h1>Blog index</h1><div class="blog-preview"><h2>Project Euler in Rust</h2><span class="blog-preview-date">Friday, 19 September 2025</span><p>After writing the obligatory &quot;Hello, World!&quot;, I wanted to work on something
more substantial in Rust. When I learn a new programming language, my go-to
source of coding problems to try and solve in the new language is
<a rel="noopener noreferrer" target="_blank" class="external-link" href="https://projecteuler.net/">Project Euler</a>, which features hundreds of coding challenges of
various difficulty levels. <a href="/Blog/20250919-rust-euler">Continue reading...</a></p></div><div class="blog-preview"><h2>&quot;Hello, World!&quot; in Rust</h2><span class="blog-preview-date">Friday, 12 September 2025</span><p>Yesterday, I started to teach myself how to program in Rust. As always, the first
step was a &quot;Hello, World!&quot; program. <a href="/Blog/20250912-hello-world-rust">Continue reading...</a></p></div><div class="blog-preview"><h2>First Rust Patch</h2><span class="blog-preview-date">Saturday, 06 September 2025</span><p>As part of preparing for my Longhorn PHP talk about what is new in PHP 8.5, I
have been going through various PHP tools and checking to what extent they
support the new features. When the support is lacking, I have sometimes tried to
Expand Down