SlideShare a Scribd company logo
Learning Rust
Henry Schreiner 1-10-2024
with Advent of Code 2023
What sort of language is Rust?
What sort of language is Rust?
High level or low level?
What sort of language is Rust?
High level or low level?
Object Oriented?
Low Level
Allowed in Linux Kernel (C)
Native embedded support
No exception handling (C++)
No garbage collector (Go)
High Level
Zero cost abstractions (C++)
Functional elements
Trait system
Syntactic macros
OOP? Depends…
OOP? Depends…
No inheritance
OOP? Depends…
No inheritance
Traits (like Protocols/Interfaces)
Syntactic macros
Why Rust?
Why Rust?
Packaging
Why Rust?
Packaging
Modern without legacy (C++23 - C++98)
Why Rust?
Packaging
Modern without legacy (C++23 - C++98)
Memory Safety (compile time)
Why Rust?
Packaging
Modern without legacy (C++23 - C++98)
Memory Safety (compile time)
Explicit error handling
Why Rust?
Packaging
Modern without legacy (C++23 - C++98)
Memory Safety (compile time)
Explicit error handling
Modern tech stacks
Why Rust?
Packaging
Modern without legacy (C++23 - C++98)
Memory Safety (compile time)
Explicit error handling
Modern tech stacks
Traits
Why Rust?
Packaging
Modern without legacy (C++23 - C++98)
Memory Safety (compile time)
Explicit error handling
Modern tech stacks
Traits
Declarative programming
Why Rust?
Packaging
Modern without legacy (C++23 - C++98)
Memory Safety (compile time)
Explicit error handling
Modern tech stacks
Traits
Declarative programming
Crabs are cuter than snakes
Ruff: an exemplary project
0s 20s 40s 60s
Ruff
Autoflake
Flake8
Pyflakes
Pycodestyle
Pylint
0.29s
6.18s
12.26s
15.79s
46.92s
> 60s
Cargo
Cargo
Simple con
f
iguration
[package]
name = "foo"
version = "0.1.0"
edition = "2021"
[dependencies]
Cargo
Simple con
f
Dependencies work!
[package]
name = "foo"
version = "0.1.0"
edition = "2021"
[dependencies]
Cargo
Simple con
f
Dependencies work!
Locks by default
Cargo
Simple con
f
Dependencies work!
Locks by default
IDEs support it
Cargo
Simple con
f
Dependencies work!
Locks by default
IDEs support it
Everyone uses it
Cargo
Simple con
f
Dependencies work!
Locks by default
IDEs support it
Everyone uses it
Alsorustup:
Easy version selection
&
nightlies
Cargo commands
Cargo commands
New
cargo new foo
Created binary (application) `foo` package
Cargo commands
New
Run
cargo new foo
Created binary (application) `foo` package
cargo run --bin foo
Compiling foo v0.1.0 (/Users/henryschreiner/tmp/foo)
Finished dev [unoptimized + debuginfo] target(s) in 0.21s
Running `target/debug/foo`
Hello, world!
Cargo commands
New
Run
Tests cargo test
Compiling foo v0.1.0 (/Users/henryschreiner/tmp/foo)
Finished test [unoptimized + debuginfo] target(s) in 2.53s
Running unittests src/main.rs (target/debug/deps/foo-f51fd73e2da0c0ec)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Cargo commands
New
Run
Tests
Benchmarking
cargo test
Compiling foo v0.1.0 (/Users/henryschreiner/tmp/foo)
Finished test [unoptimized + debuginfo] target(s) in 2.53s
Running unittests src/main.rs (target/debug/deps/foo-f51fd73e2da0c0ec)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
cargo bench
Compiling foo v0.1.0 (/Users/henryschreiner/tmp/foo)
Finished bench [optimized] target(s) in 0.20s
Running unittests src/main.rs (target/release/deps/foo-140fab48e69ea289)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Cargo commands
New
Run
Tests
Benchmarking
Docs
Cargo commands
New
Run
Tests
Benchmarking
Docs
Formatting
cargo fmt
Cargo commands
New
Run
Tests
Benchmarking
Docs
Formatting
Linting
cargo clippy --fix --all
Checking foo v0.1.0 (/Users/henryschreiner/tmp/foo)
Finished dev [unoptimized + debuginfo] target(s) in 0.59s
cargo fmt
Cargo commands
New
Run
Tests
Benchmarking
Docs
Formatting
Linting
Plugins
Expand
Show code generation
Unused features
Find features not being used
Nextest
Fancier testing interface
Audit
Find security vulnerabilities
Udeps
Find unused dependencies
Readme
Make README.md from docs
Demo
New package
Photo by Redaviqui Davilli on Unsplash
Modern design
no legacy
Enums
Modern enum +
std::variant (C++17)
Enum + Union (Python)
Use statements (including *)
Modern design
no legacy
Enums
Modern enum +
std::variant (C++17)
Enum + Union (Python)
Use statements (including *)
enum Direction {
North,
West,
East,
South,
}
Modern design
no legacy
Enums
Modern enum +
std::variant (C++17)
Enum + Union (Python)
Use statements (including *)
enum Direction {
North,
West,
East,
South,
}
enum Value {
Int(i32),
Float(f64),
}
Modern design
no legacy
Enums
Modern enum +
std::variant (C++17)
Enum + Union (Python)
Use statements (including *)
enum Direction {
North,
West,
East,
South,
}
enum Option<T> {
Some(T),
None,
}
(Don’t actually write this, it’s built in!)
enum Value {
Int(i32),
Float(f64),
}
Modern design
no legacy
Pattern matching
Perfect with enums
Exhaustive
Shortcut “if let”
Modern design
no legacy
Pattern matching
Perfect with enums
Exhaustive
Shortcut “if let”
let val = match dir {
North => 1,
West => 2,
East => 3,
South => 4,
}
Modern design
no legacy
Pattern matching
Perfect with enums
Exhaustive
Shortcut “if let”
let val = match dir {
North => 1,
West => 2,
East => 3,
South => 4,
}
m
m
m
m
m
p
p
p
p
p
p
p
p
("
"
v}
}
)
)
}
Modern design
no legacy
Pattern matching
Perfect with enums
Exhaustive
Shortcut “if let”
let val = match dir {
North => 1,
West => 2,
East => 3,
South => 4,
}
if let Some(v) = opt {
println!(“{v}");
}
Modern design
no legacy
Error handling
No exceptions (catching)
Panic (uncatchable exit)
Can unwind (default) or abort
Error enum (C++23)
Shortcut: ?
(on Option too)
Modern design
no legacy
Error handling
No exceptions (catching)
Panic (uncatchable exit)
Can unwind (default) or abort
Error enum (C++23)
Shortcut: ?
(on Option too)
panic!("Goodby");
Modern design
no legacy
Error handling
No exceptions (catching)
Panic (uncatchable exit)
Can unwind (default) or abort
Error enum (C++23)
Shortcut: ?
(on Option too)
panic!("Goodby");
fn f(x: i32) -> Option<u32> {
if x >= 0 {
Some(x as u32)
} else {
None
}
}
Modern design
no legacy
Error handling
No exceptions (catching)
Panic (uncatchable exit)
Can unwind (default) or abort
Error enum (C++23)
Shortcut: ?
(on Option too)
panic!("Goodby");
fn f(x: i32) -> Option<u32> {
if x >= 0 {
Some(x as u32)
} else {
None
}
}
fn g(x: i32) -> Option<u32> {
Some(f(x)?)
}
Modern design
no legacy
Moves
Move (C++11) by default
Explicit clones & references
Slices (C++17)
Modern design
no legacy
Moves
Move (C++11) by default
Explicit clones & references
Slices (C++17)
let s = "hello".to_string();
Modern design
no legacy
Moves
Move (C++11) by default
Explicit clones & references
Slices (C++17)
let s = "hello".to_string();
f(s);
// s now invalid!
Modern design
no legacy
Moves
Move (C++11) by default
Explicit clones & references
Slices (C++17)
let s = "hello".to_string();
f(s);
// s now invalid!
f(s.clone());
// Explicit copy
// Some types support implicit
Modern design
no legacy
Moves
Move (C++11) by default
Explicit clones & references
Slices (C++17)
let s = "hello".to_string();
f(s);
// s now invalid!
f(s.clone());
// Explicit copy
// Some types support implicit
f(&s);
// Immutable reference
Modern design
no legacy
Moves
Move (C++11) by default
Explicit clones & references
Slices (C++17)
let s = "hello".to_string();
f(s);
// s now invalid!
f(s.clone());
// Explicit copy
// Some types support implicit
f(&s);
// Immutable reference
Functions can even take &str,
which is a generic string slice!
Modern design
no legacy
Constraints required
Constraints (C++20) required
Great error messages
Modern design
no legacy
Constraints required
Constraints (C++20) required
Great error messages
use std::vec::Vec;
fn sum<T>(v: &Vec<T>) -> T {
v.iter().sum()
}
u
u
u
u
u
u
f
f
}
Modern design
no legacy
Constraints required
Constraints (C++20) required
Great error messages
use std::vec::Vec;
fn sum<T>(v: &Vec<T>) -> T {
v.iter().sum()
}
u
u
u
u
u
u
f
f
}
Modern design
no legacy
Constraints required
Constraints (C++20) required
Great error messages
use std::vec::Vec;
fn sum<T>(v: &Vec<T>) -> T {
v.iter().sum()
}
Entirely following instructions
from the compiler!
Modern design
no legacy
Constraints required
Constraints (C++20) required
Great error messages
use std::vec::Vec;
fn sum<T>(v: &Vec<T>) -> T {
v.iter().sum()
}
use std::iter::Sum;
fn sum<T: for<'a> Sum<&'a T>>(v: &[T]) -> T {
v.iter().sum()
}
Entirely following instructions
from the compiler!
Modern design
no legacy
Module system
Modules (C++20, C++23)
Explicit public interface
(private by default)
Modern design
no legacy
Module system
Modules (C++20, C++23)
Explicit public interface
(private by default)
Basically nothing to show, it just works.
No import statements required.
Memory safety
at compile time!
Ownership
Tracked at compile time
Lifetimes
Always tracked, sometimes explicit
Single mutable ref
Can’t have a const ref too!
Unsafe blocks
Can move safety checks to runtime
Only one of the three major C++ successor
languages (Val) is attempting this!
Library utilities
Rc, Weak, Box, Cell, RefCell, …
Modern needs
designed in!
Great Python Support
WebAssembly
Embedded systems
Single binary
No GLIBC required
Cross-compiles
Syntactic Macros
Rust’s secret sauce
Almost like re
f
lection (C++26?)
Fully scoped
Support variable arguments
Simple inline version too
Can be written in Rust!
Access tokenizer and AST
println!("Hello")
vec![1,2,3]
#[derive(Debug)]
struct A {}
#[cfg(test)]
struct A {}
All items can have attributes, too!
Declarative programming
AoC Day 10
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::EnumString, strum::Display)]
enum MapChar {
#[strum(serialize = "S")]
Start,
#[strum(serialize = "|", to_string = "│")]
Vertical,
#[strum(serialize = "-", to_string = "─")]
Horizontal,
#[strum(serialize = "J", to_string = "╯")]
UpLeft,
#[strum(serialize = "L", to_string = "╰")]
UpRight,
#[strum(serialize = "7", to_string = "╮")]
DownLeft,
#[strum(serialize = "F", to_string = "╭")]
DownRight,
#[strum(serialize = ".", to_string = "•")]
Empty,
}
7-F7-
.FJ|7
SJLL7
|F--J
LJ.LJ
╮─╭╮─
•╭╯│╮
S╯╰╰╮
│╭──╯
╰╯•╰╯
Learning Rust with Advent of Code 2023 - Princeton
Traits
Protocol++
use core::ops::Add;
#[derive(Debug)]
struct Vector {
x: f64,
y: f64,
}
impl Add for Vector {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
fn main() {
let a = Vector{x: 1.0, y: 2.0};
let b = Vector{x: 3.0, y: 4.0};
let c = a + b;
// Or let c = a.add(b);
println!("{c:?}");
}
Explicit opt-in
Method-like syntax
Derive macro
Explicit scoping
Rule: Must “own”
either trait or type
What is Advent of Code?
Just in case you haven’t heard of it
• 25 structured word problems expecting code-based solutions
• Example test input
• A per-user unique generated input
• A (usually large) integer answer, time spaced retries
• A second part unlocked by answering the
f
irst, same input
• Competitive ranking based on time to solution, released at midnight
• ~1/4 million people attempt every year, 10K or so
f
inish
• Often unusual variations on known problems (helps avoid AI solutions)
Advent of Code to learn a language
Pros and cons
• Code should solve problems
• Wide range of algorithms
• Extensive solutions available after the leaderboard
f
ills
• Libraries not required but often helpful
• Nothing too slow if done right
• All share similar structure (text in, int out)
• Hard to be competitive in a new language
henryiii/aoc2023
Repository details
Solutions to all 25 days of AoC 2023 in Rust
Most are stand-alone solutions (a couple share via lib)
Balance between speed and readability
Trying di
ff
erent things, libraries, styles
Some documentation
Some have Python versions in docs
Many have various versions in Git history
Day 1
Sum
f
irst + last
fn number_line(line: &str) -> u32 {
let mut chars = line.chars().filter_map(|c| c.to_digit(10));
let start = chars.next().unwrap();
let end = chars.last().unwrap_or(start);
10 * start + end
}
fn main() {
let text = std::fs::read_to_string("01.txt").unwrap();
let sum: u32 = text.lines().map(number_line).sum();
println!("Sum: {sum}");
}
#[cfg(test)]
mod tests {
use super::*;
const INPUT: &str = "
1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchetn";
#[test]
fn test_01() {
let sum: u32 = INPUT.lines().map(number_line).sum();
assert_eq!(sum, 142);
}
}
Only docs are removed,
otherwise this is the
whole
f
ile!
src/bin/01.rs gets
picked up automatically
by Cargo
Day 5
Optional progress bar
#[cfg(feature = "progressbar")]
use indicatif::ProgressIterator;
/// …
#[cfg(feature = "progressbar")]
let seed_iter = seed_iter.progress_count(
seeds.iter().skip(1).step_by(2).sum()
);
Day 7
Card game
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)]
enum StdCard {
#[strum(serialize = "2")]
Two,
#[strum(serialize = "3")]
Three
// …
} // Also JokerCard
Day 7
Card game
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)]
enum StdCard {
#[strum(serialize = "2")]
Two,
#[strum(serialize = "3")]
Three
// …
} // Also JokerCard
trait Card: Hash + Eq + Copy + Debug + Ord + FromStr {
fn is_joker(&self) -> bool;
}
Day 7
Card game
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)]
enum StdCard {
#[strum(serialize = "2")]
Two,
#[strum(serialize = "3")]
Three
// …
} // Also JokerCard
impl Card for JokerCard {
fn is_joker(&self) -> bool {
matches!(self, Self::Joker)
}
}
trait Card: Hash + Eq + Copy + Debug + Ord + FromStr {
fn is_joker(&self) -> bool;
}
impl Card for StdCard {
fn is_joker(&self) -> bool {
false
}
}
Day 19
Parser
mod my_parser {
use pest_derive::Parser;
#[derive(Parser)]
#[grammar_inline = r#"
eoi = _{ !ANY }
cat = { "x" | "m" | "a" | "s" }
compare = { ("<" | ">") ~ ASCII_DIGIT+ }
ident = { ASCII_ALPHA_LOWER+ }
target = { ident | "A" | "R" }
single_rule = { cat ~ compare ~ ":" ~ target }
rules = { (single_rule ~ ",")+ }
line = { ident ~ "{" ~ rules ~ target ~ "}" }
file = { SOI ~ (line ~ NEWLINE*)* ~ eoi }
"#]
pub struct MyParser;
}
IDEs understand the generated code!
Day 22
3D block tower
fn compute1(text: &str) -> usize {
let mut blocks = read(text);
blocks.sort();
lower_blocks(&mut blocks);
removable_blocks(&blocks).len()
}
Setting up Blocks took ~100 lines (omitted!)
Rendered in Blender with Python
Day 22
3D block tower
fn compute1(text: &str) -> usize {
let mut blocks = read(text);
blocks.sort();
lower_blocks(&mut blocks);
removable_blocks(&blocks).len()
}
Setting up Blocks took ~100 lines (omitted!)
Rendered in Blender with Python
Day 22
3D block tower
fn compute2(text: &str) -> usize {
let mut blocks = read(text);
blocks.sort();
lower_blocks(&mut blocks);
blocks
.iter()
.map(|b| {
let mut new_blocks: Vec<Block> =
blocks.iter().filter(|x| *x != b).cloned().collect();
lower_blocks(&mut new_blocks);
blocks
.iter()
.filter(|x| *x != b)
.zip(new_blocks.iter())
.filter(|(x, y)| **x != **y)
.count()
})
.sum()
}
Day 24b
Worst Python - Rust comparison
from pathlib import Path
import sympy
def read(fn):
txt = Path(fn).read_text()
lines = [t.replace("@", " ").split() for t in txt.splitlines()]
return [tuple(int(x.strip(",")) for x in a) for a in lines]
px, py, pz, dx, dy, dz = sympy.symbols("px, py, pz, dx, dy, dz", integer=True)
vals = read("24data.txt")
eqs = []
for pxi, pyi, pzi, dxi, dyi, dzi in vals[:3]:
eqs.append((pxi - px) * (dy - dyi) - (pyi - py) * (dx - dxi))
eqs.append((pyi - py) * (dz - dzi) - (pzi - pz) * (dy - dyi))
answer = sympy.solve(eqs)
print(answer)
This is the whole Python solution
Resorted to tricks to keep Rust solution manageable
Thoughts on Rust
Generally very positive!
• Loved the developer experience with Cargo
• Maxed out clippy (linter) with nursery lints and more
• Good support for functional programming was great
• Loved the Trait system
• Code wasn’t concise, but clear and fun to write
• Had to
f
ight Rust occasionally, but code was better for it
• Had zero segfaults. Zero.
Thoughts on Rust
A few downsides
• Code was a lot longer than the Python version
• Code sometimes slower than the Python version (higher level libs)
• But found pretty good libs with ports of the Python algorithms
sometimes
• Much younger ecosystem than Python (but there _is_ one, unlike C++/C)
• Dependencies are “normal”, so can’t just use stdlib (good and bad)
• Some missing features, but it was easy to work around
• No generators or ternaries, for example
When would I use Rust?
Just some ideas
• For command line / developer tools
• Startup speed is unbelievable compared to any interpreter
• Options exist for unicode handling (unlike, say, C++…)
• For Python extensions (Python integration is top-notch)
• For things that don’t need CUDA
• When targeting WebAssembly
• If not needing libraries in other languages (though it has cross-compat)
Rust compared to other languages
A few, anyway
• You can write a library that depends on another library! (C++)
• Editions can work together (C++)
• No/little bad legacy code (like templates without constraints) (C++)
• Lazy functional, multiline closures, Traits, and explicit errors (Python)
• First-class support for features and pro
f
iles (C++ or Python)
Useful Links
adventofcode.com
blessed.rs
play.rust-lang.org
doc.rust-lang.org/book
henryiii.github.io/aoc2023
Photo by Chandler Cruttenden on Unsplash
More info?
Join the RSE Rust learning
group!
Bonus: Day 22
Python - Blender viz
import bpy
import bmesh
from mathutils import Matrix
import numpy as np
TXT = """
1,0,1~1,2,1
0,0,2~2,0,2
0,2,3~2,2,3
0,0,4~0,2,4
2,0,5~2,2,5
0,1,6~2,1,6
1,1,8~1,1,9"""
def add_object(bm, start, end):
scale = np.abs(start - end) + 1
matrix = Matrix.LocRotScale((start + end) / 2, None, scale)
bmesh.ops.create_cube(bm, size=1.0, matrix=matrix)
bm = bmesh.new()
for line in TXT.splitlines():
ax, ay, az, bx, by, bz = map(float, line.replace("~", ",").split(","))
add_object(bm, np.array((ax, ay, az)), np.array((bx, by, bz)))
me = bpy.data.meshes.new("Mesh")
bm.to_mesh(me)
bm.free()
obj = bpy.data.objects.new("Object", me)
bpy.context.collection.objects.link(obj)

More Related Content

PPTX
How to Adopt Modern C++17 into Your C++ Code
PPTX
How to Adopt Modern C++17 into Your C++ Code
PPT
为什么 rust-lang 吸引我?
PPT
Concurrency in go
PPTX
cs262_intro_slides.pptx
ODP
The why and how of moving to PHP 5.5/5.6
PDF
Kyo - Functional Scala 2023.pdf
PPT
Implementation of 'go-like' language constructions in scala [english version]
How to Adopt Modern C++17 into Your C++ Code
How to Adopt Modern C++17 into Your C++ Code
为什么 rust-lang 吸引我?
Concurrency in go
cs262_intro_slides.pptx
The why and how of moving to PHP 5.5/5.6
Kyo - Functional Scala 2023.pdf
Implementation of 'go-like' language constructions in scala [english version]

Similar to Learning Rust with Advent of Code 2023 - Princeton (20)

ODP
Java Generics
PPTX
Price of an Error
PDF
Csw2016 gawlik bypassing_differentdefenseschemes
PPTX
Introduction to Rust language programming
PPTX
REverse_SAILR_talk.pptx buccaneers of the binary
PDF
New features in Ruby 2.5
ODP
The why and how of moving to php 5.4/5.5
PDF
[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...
PPTX
golang_getting_started.pptx
ODP
Java 5 6 Generics, Concurrency, Garbage Collection, Tuning
ODP
ooc - A hybrid language experiment
ODP
ooc - A hybrid language experiment
PDF
Go 1.10 Release Party - PDX Go
PPT
Os Vanrossum
PDF
2015-GopherCon-Talk-Uptime.pdf
PDF
CRAXweb: Automatic web application testing and attack generation
PDF
The Ring programming language version 1.8 book - Part 95 of 202
PDF
Advanced Python, Part 2
PDF
Goroutines and Channels in practice
PDF
basicsCC++1.pdf in Pakistan and its parts
Java Generics
Price of an Error
Csw2016 gawlik bypassing_differentdefenseschemes
Introduction to Rust language programming
REverse_SAILR_talk.pptx buccaneers of the binary
New features in Ruby 2.5
The why and how of moving to php 5.4/5.5
[C++] The Curiously Recurring Template Pattern: Static Polymorphsim and Expre...
golang_getting_started.pptx
Java 5 6 Generics, Concurrency, Garbage Collection, Tuning
ooc - A hybrid language experiment
ooc - A hybrid language experiment
Go 1.10 Release Party - PDX Go
Os Vanrossum
2015-GopherCon-Talk-Uptime.pdf
CRAXweb: Automatic web application testing and attack generation
The Ring programming language version 1.8 book - Part 95 of 202
Advanced Python, Part 2
Goroutines and Channels in practice
basicsCC++1.pdf in Pakistan and its parts
Ad

More from Henry Schreiner (20)

PDF
SciPy 2025 - Packaging a Scientific Python Project
PDF
Tools That Help You Write Better Code - 2025 Princeton Software Engineering S...
PDF
Princeton RSE: Building Python Packages (+binary)
PDF
Tools to help you write better code - Princeton Wintersession
PDF
The two flavors of Python 3.13 - PyHEP 2024
PDF
Modern binary build systems - PyCon 2024
PDF
Software Quality Assurance Tooling - Wintersession 2024
PDF
Princeton RSE Peer network first meeting
PDF
Software Quality Assurance Tooling 2023
PDF
Princeton Wintersession: Software Quality Assurance Tooling
PDF
What's new in Python 3.11
PDF
Everything you didn't know you needed
PDF
SciPy22 - Building binary extensions with pybind11, scikit build, and cibuild...
PDF
SciPy 2022 Scikit-HEP
PDF
PyCon 2022 -Scikit-HEP Developer Pages: Guidelines for modern packaging
PDF
PyCon2022 - Building Python Extensions
PDF
boost-histogram / Hist: PyHEP Topical meeting
PDF
Digital RSE: automated code quality checks - RSE group meeting
PDF
CMake best practices
PDF
Pybind11 - SciPy 2021
SciPy 2025 - Packaging a Scientific Python Project
Tools That Help You Write Better Code - 2025 Princeton Software Engineering S...
Princeton RSE: Building Python Packages (+binary)
Tools to help you write better code - Princeton Wintersession
The two flavors of Python 3.13 - PyHEP 2024
Modern binary build systems - PyCon 2024
Software Quality Assurance Tooling - Wintersession 2024
Princeton RSE Peer network first meeting
Software Quality Assurance Tooling 2023
Princeton Wintersession: Software Quality Assurance Tooling
What's new in Python 3.11
Everything you didn't know you needed
SciPy22 - Building binary extensions with pybind11, scikit build, and cibuild...
SciPy 2022 Scikit-HEP
PyCon 2022 -Scikit-HEP Developer Pages: Guidelines for modern packaging
PyCon2022 - Building Python Extensions
boost-histogram / Hist: PyHEP Topical meeting
Digital RSE: automated code quality checks - RSE group meeting
CMake best practices
Pybind11 - SciPy 2021
Ad

Recently uploaded (20)

PPTX
history of c programming in notes for students .pptx
PDF
How Creative Agencies Leverage Project Management Software.pdf
PPT
Introduction Database Management System for Course Database
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PDF
Understanding Forklifts - TECH EHS Solution
PDF
Nekopoi APK 2025 free lastest update
PPTX
Online Work Permit System for Fast Permit Processing
PDF
System and Network Administraation Chapter 3
PDF
Digital Strategies for Manufacturing Companies
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PPTX
Introduction to Artificial Intelligence
PPTX
L1 - Introduction to python Backend.pptx
PDF
medical staffing services at VALiNTRY
PDF
AI in Product Development-omnex systems
PDF
PTS Company Brochure 2025 (1).pdf.......
PPTX
ai tools demonstartion for schools and inter college
PDF
Upgrade and Innovation Strategies for SAP ERP Customers
PPTX
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PDF
Design an Analysis of Algorithms II-SECS-1021-03
history of c programming in notes for students .pptx
How Creative Agencies Leverage Project Management Software.pdf
Introduction Database Management System for Course Database
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Understanding Forklifts - TECH EHS Solution
Nekopoi APK 2025 free lastest update
Online Work Permit System for Fast Permit Processing
System and Network Administraation Chapter 3
Digital Strategies for Manufacturing Companies
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Introduction to Artificial Intelligence
L1 - Introduction to python Backend.pptx
medical staffing services at VALiNTRY
AI in Product Development-omnex systems
PTS Company Brochure 2025 (1).pdf.......
ai tools demonstartion for schools and inter college
Upgrade and Innovation Strategies for SAP ERP Customers
CHAPTER 12 - CYBER SECURITY AND FUTURE SKILLS (1) (1).pptx
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
Design an Analysis of Algorithms II-SECS-1021-03

Learning Rust with Advent of Code 2023 - Princeton

  • 1. Learning Rust Henry Schreiner 1-10-2024 with Advent of Code 2023
  • 2. What sort of language is Rust?
  • 3. What sort of language is Rust? High level or low level?
  • 4. What sort of language is Rust? High level or low level? Object Oriented?
  • 5. Low Level Allowed in Linux Kernel (C) Native embedded support No exception handling (C++) No garbage collector (Go) High Level Zero cost abstractions (C++) Functional elements Trait system Syntactic macros
  • 8. OOP? Depends… No inheritance Traits (like Protocols/Interfaces) Syntactic macros
  • 11. Why Rust? Packaging Modern without legacy (C++23 - C++98)
  • 12. Why Rust? Packaging Modern without legacy (C++23 - C++98) Memory Safety (compile time)
  • 13. Why Rust? Packaging Modern without legacy (C++23 - C++98) Memory Safety (compile time) Explicit error handling
  • 14. Why Rust? Packaging Modern without legacy (C++23 - C++98) Memory Safety (compile time) Explicit error handling Modern tech stacks
  • 15. Why Rust? Packaging Modern without legacy (C++23 - C++98) Memory Safety (compile time) Explicit error handling Modern tech stacks Traits
  • 16. Why Rust? Packaging Modern without legacy (C++23 - C++98) Memory Safety (compile time) Explicit error handling Modern tech stacks Traits Declarative programming
  • 17. Why Rust? Packaging Modern without legacy (C++23 - C++98) Memory Safety (compile time) Explicit error handling Modern tech stacks Traits Declarative programming Crabs are cuter than snakes
  • 18. Ruff: an exemplary project 0s 20s 40s 60s Ruff Autoflake Flake8 Pyflakes Pycodestyle Pylint 0.29s 6.18s 12.26s 15.79s 46.92s > 60s
  • 19. Cargo
  • 20. Cargo Simple con f iguration [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies]
  • 21. Cargo Simple con f Dependencies work! [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies]
  • 23. Cargo Simple con f Dependencies work! Locks by default IDEs support it
  • 24. Cargo Simple con f Dependencies work! Locks by default IDEs support it Everyone uses it
  • 25. Cargo Simple con f Dependencies work! Locks by default IDEs support it Everyone uses it Alsorustup: Easy version selection & nightlies
  • 27. Cargo commands New cargo new foo Created binary (application) `foo` package
  • 28. Cargo commands New Run cargo new foo Created binary (application) `foo` package cargo run --bin foo Compiling foo v0.1.0 (/Users/henryschreiner/tmp/foo) Finished dev [unoptimized + debuginfo] target(s) in 0.21s Running `target/debug/foo` Hello, world!
  • 29. Cargo commands New Run Tests cargo test Compiling foo v0.1.0 (/Users/henryschreiner/tmp/foo) Finished test [unoptimized + debuginfo] target(s) in 2.53s Running unittests src/main.rs (target/debug/deps/foo-f51fd73e2da0c0ec) running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
  • 30. Cargo commands New Run Tests Benchmarking cargo test Compiling foo v0.1.0 (/Users/henryschreiner/tmp/foo) Finished test [unoptimized + debuginfo] target(s) in 2.53s Running unittests src/main.rs (target/debug/deps/foo-f51fd73e2da0c0ec) running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s cargo bench Compiling foo v0.1.0 (/Users/henryschreiner/tmp/foo) Finished bench [optimized] target(s) in 0.20s Running unittests src/main.rs (target/release/deps/foo-140fab48e69ea289) running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
  • 33. Cargo commands New Run Tests Benchmarking Docs Formatting Linting cargo clippy --fix --all Checking foo v0.1.0 (/Users/henryschreiner/tmp/foo) Finished dev [unoptimized + debuginfo] target(s) in 0.59s cargo fmt
  • 34. Cargo commands New Run Tests Benchmarking Docs Formatting Linting Plugins Expand Show code generation Unused features Find features not being used Nextest Fancier testing interface Audit Find security vulnerabilities Udeps Find unused dependencies Readme Make README.md from docs
  • 35. Demo New package Photo by Redaviqui Davilli on Unsplash
  • 36. Modern design no legacy Enums Modern enum + std::variant (C++17) Enum + Union (Python) Use statements (including *)
  • 37. Modern design no legacy Enums Modern enum + std::variant (C++17) Enum + Union (Python) Use statements (including *) enum Direction { North, West, East, South, }
  • 38. Modern design no legacy Enums Modern enum + std::variant (C++17) Enum + Union (Python) Use statements (including *) enum Direction { North, West, East, South, } enum Value { Int(i32), Float(f64), }
  • 39. Modern design no legacy Enums Modern enum + std::variant (C++17) Enum + Union (Python) Use statements (including *) enum Direction { North, West, East, South, } enum Option<T> { Some(T), None, } (Don’t actually write this, it’s built in!) enum Value { Int(i32), Float(f64), }
  • 40. Modern design no legacy Pattern matching Perfect with enums Exhaustive Shortcut “if let”
  • 41. Modern design no legacy Pattern matching Perfect with enums Exhaustive Shortcut “if let” let val = match dir { North => 1, West => 2, East => 3, South => 4, }
  • 42. Modern design no legacy Pattern matching Perfect with enums Exhaustive Shortcut “if let” let val = match dir { North => 1, West => 2, East => 3, South => 4, } m m m m m p p p p p p p p (" " v} } ) ) }
  • 43. Modern design no legacy Pattern matching Perfect with enums Exhaustive Shortcut “if let” let val = match dir { North => 1, West => 2, East => 3, South => 4, } if let Some(v) = opt { println!(“{v}"); }
  • 44. Modern design no legacy Error handling No exceptions (catching) Panic (uncatchable exit) Can unwind (default) or abort Error enum (C++23) Shortcut: ? (on Option too)
  • 45. Modern design no legacy Error handling No exceptions (catching) Panic (uncatchable exit) Can unwind (default) or abort Error enum (C++23) Shortcut: ? (on Option too) panic!("Goodby");
  • 46. Modern design no legacy Error handling No exceptions (catching) Panic (uncatchable exit) Can unwind (default) or abort Error enum (C++23) Shortcut: ? (on Option too) panic!("Goodby"); fn f(x: i32) -> Option<u32> { if x >= 0 { Some(x as u32) } else { None } }
  • 47. Modern design no legacy Error handling No exceptions (catching) Panic (uncatchable exit) Can unwind (default) or abort Error enum (C++23) Shortcut: ? (on Option too) panic!("Goodby"); fn f(x: i32) -> Option<u32> { if x >= 0 { Some(x as u32) } else { None } } fn g(x: i32) -> Option<u32> { Some(f(x)?) }
  • 48. Modern design no legacy Moves Move (C++11) by default Explicit clones & references Slices (C++17)
  • 49. Modern design no legacy Moves Move (C++11) by default Explicit clones & references Slices (C++17) let s = "hello".to_string();
  • 50. Modern design no legacy Moves Move (C++11) by default Explicit clones & references Slices (C++17) let s = "hello".to_string(); f(s); // s now invalid!
  • 51. Modern design no legacy Moves Move (C++11) by default Explicit clones & references Slices (C++17) let s = "hello".to_string(); f(s); // s now invalid! f(s.clone()); // Explicit copy // Some types support implicit
  • 52. Modern design no legacy Moves Move (C++11) by default Explicit clones & references Slices (C++17) let s = "hello".to_string(); f(s); // s now invalid! f(s.clone()); // Explicit copy // Some types support implicit f(&s); // Immutable reference
  • 53. Modern design no legacy Moves Move (C++11) by default Explicit clones & references Slices (C++17) let s = "hello".to_string(); f(s); // s now invalid! f(s.clone()); // Explicit copy // Some types support implicit f(&s); // Immutable reference Functions can even take &str, which is a generic string slice!
  • 54. Modern design no legacy Constraints required Constraints (C++20) required Great error messages
  • 55. Modern design no legacy Constraints required Constraints (C++20) required Great error messages use std::vec::Vec; fn sum<T>(v: &Vec<T>) -> T { v.iter().sum() }
  • 56. u u u u u u f f } Modern design no legacy Constraints required Constraints (C++20) required Great error messages use std::vec::Vec; fn sum<T>(v: &Vec<T>) -> T { v.iter().sum() }
  • 57. u u u u u u f f } Modern design no legacy Constraints required Constraints (C++20) required Great error messages use std::vec::Vec; fn sum<T>(v: &Vec<T>) -> T { v.iter().sum() } Entirely following instructions from the compiler!
  • 58. Modern design no legacy Constraints required Constraints (C++20) required Great error messages use std::vec::Vec; fn sum<T>(v: &Vec<T>) -> T { v.iter().sum() } use std::iter::Sum; fn sum<T: for<'a> Sum<&'a T>>(v: &[T]) -> T { v.iter().sum() } Entirely following instructions from the compiler!
  • 59. Modern design no legacy Module system Modules (C++20, C++23) Explicit public interface (private by default)
  • 60. Modern design no legacy Module system Modules (C++20, C++23) Explicit public interface (private by default) Basically nothing to show, it just works. No import statements required.
  • 61. Memory safety at compile time! Ownership Tracked at compile time Lifetimes Always tracked, sometimes explicit Single mutable ref Can’t have a const ref too! Unsafe blocks Can move safety checks to runtime Only one of the three major C++ successor languages (Val) is attempting this! Library utilities Rc, Weak, Box, Cell, RefCell, …
  • 62. Modern needs designed in! Great Python Support WebAssembly Embedded systems Single binary No GLIBC required Cross-compiles
  • 63. Syntactic Macros Rust’s secret sauce Almost like re f lection (C++26?) Fully scoped Support variable arguments Simple inline version too Can be written in Rust! Access tokenizer and AST println!("Hello") vec![1,2,3] #[derive(Debug)] struct A {} #[cfg(test)] struct A {} All items can have attributes, too!
  • 64. Declarative programming AoC Day 10 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum::EnumString, strum::Display)] enum MapChar { #[strum(serialize = "S")] Start, #[strum(serialize = "|", to_string = "│")] Vertical, #[strum(serialize = "-", to_string = "─")] Horizontal, #[strum(serialize = "J", to_string = "╯")] UpLeft, #[strum(serialize = "L", to_string = "╰")] UpRight, #[strum(serialize = "7", to_string = "╮")] DownLeft, #[strum(serialize = "F", to_string = "╭")] DownRight, #[strum(serialize = ".", to_string = "•")] Empty, } 7-F7- .FJ|7 SJLL7 |F--J LJ.LJ ╮─╭╮─ •╭╯│╮ S╯╰╰╮ │╭──╯ ╰╯•╰╯
  • 66. Traits Protocol++ use core::ops::Add; #[derive(Debug)] struct Vector { x: f64, y: f64, } impl Add for Vector { type Output = Self; fn add(self, other: Self) -> Self { Self { x: self.x + other.x, y: self.y + other.y, } } } fn main() { let a = Vector{x: 1.0, y: 2.0}; let b = Vector{x: 3.0, y: 4.0}; let c = a + b; // Or let c = a.add(b); println!("{c:?}"); } Explicit opt-in Method-like syntax Derive macro Explicit scoping Rule: Must “own” either trait or type
  • 67. What is Advent of Code? Just in case you haven’t heard of it • 25 structured word problems expecting code-based solutions • Example test input • A per-user unique generated input • A (usually large) integer answer, time spaced retries • A second part unlocked by answering the f irst, same input • Competitive ranking based on time to solution, released at midnight • ~1/4 million people attempt every year, 10K or so f inish • Often unusual variations on known problems (helps avoid AI solutions)
  • 68. Advent of Code to learn a language Pros and cons • Code should solve problems • Wide range of algorithms • Extensive solutions available after the leaderboard f ills • Libraries not required but often helpful • Nothing too slow if done right • All share similar structure (text in, int out) • Hard to be competitive in a new language
  • 69. henryiii/aoc2023 Repository details Solutions to all 25 days of AoC 2023 in Rust Most are stand-alone solutions (a couple share via lib) Balance between speed and readability Trying di ff erent things, libraries, styles Some documentation Some have Python versions in docs Many have various versions in Git history
  • 70. Day 1 Sum f irst + last fn number_line(line: &str) -> u32 { let mut chars = line.chars().filter_map(|c| c.to_digit(10)); let start = chars.next().unwrap(); let end = chars.last().unwrap_or(start); 10 * start + end } fn main() { let text = std::fs::read_to_string("01.txt").unwrap(); let sum: u32 = text.lines().map(number_line).sum(); println!("Sum: {sum}"); } #[cfg(test)] mod tests { use super::*; const INPUT: &str = " 1abc2 pqr3stu8vwx a1b2c3d4e5f treb7uchetn"; #[test] fn test_01() { let sum: u32 = INPUT.lines().map(number_line).sum(); assert_eq!(sum, 142); } } Only docs are removed, otherwise this is the whole f ile! src/bin/01.rs gets picked up automatically by Cargo
  • 71. Day 5 Optional progress bar #[cfg(feature = "progressbar")] use indicatif::ProgressIterator; /// … #[cfg(feature = "progressbar")] let seed_iter = seed_iter.progress_count( seeds.iter().skip(1).step_by(2).sum() );
  • 72. Day 7 Card game #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)] enum StdCard { #[strum(serialize = "2")] Two, #[strum(serialize = "3")] Three // … } // Also JokerCard
  • 73. Day 7 Card game #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)] enum StdCard { #[strum(serialize = "2")] Two, #[strum(serialize = "3")] Three // … } // Also JokerCard trait Card: Hash + Eq + Copy + Debug + Ord + FromStr { fn is_joker(&self) -> bool; }
  • 74. Day 7 Card game #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, EnumString)] enum StdCard { #[strum(serialize = "2")] Two, #[strum(serialize = "3")] Three // … } // Also JokerCard impl Card for JokerCard { fn is_joker(&self) -> bool { matches!(self, Self::Joker) } } trait Card: Hash + Eq + Copy + Debug + Ord + FromStr { fn is_joker(&self) -> bool; } impl Card for StdCard { fn is_joker(&self) -> bool { false } }
  • 75. Day 19 Parser mod my_parser { use pest_derive::Parser; #[derive(Parser)] #[grammar_inline = r#" eoi = _{ !ANY } cat = { "x" | "m" | "a" | "s" } compare = { ("<" | ">") ~ ASCII_DIGIT+ } ident = { ASCII_ALPHA_LOWER+ } target = { ident | "A" | "R" } single_rule = { cat ~ compare ~ ":" ~ target } rules = { (single_rule ~ ",")+ } line = { ident ~ "{" ~ rules ~ target ~ "}" } file = { SOI ~ (line ~ NEWLINE*)* ~ eoi } "#] pub struct MyParser; } IDEs understand the generated code!
  • 76. Day 22 3D block tower fn compute1(text: &str) -> usize { let mut blocks = read(text); blocks.sort(); lower_blocks(&mut blocks); removable_blocks(&blocks).len() } Setting up Blocks took ~100 lines (omitted!) Rendered in Blender with Python
  • 77. Day 22 3D block tower fn compute1(text: &str) -> usize { let mut blocks = read(text); blocks.sort(); lower_blocks(&mut blocks); removable_blocks(&blocks).len() } Setting up Blocks took ~100 lines (omitted!) Rendered in Blender with Python
  • 78. Day 22 3D block tower fn compute2(text: &str) -> usize { let mut blocks = read(text); blocks.sort(); lower_blocks(&mut blocks); blocks .iter() .map(|b| { let mut new_blocks: Vec<Block> = blocks.iter().filter(|x| *x != b).cloned().collect(); lower_blocks(&mut new_blocks); blocks .iter() .filter(|x| *x != b) .zip(new_blocks.iter()) .filter(|(x, y)| **x != **y) .count() }) .sum() }
  • 79. Day 24b Worst Python - Rust comparison from pathlib import Path import sympy def read(fn): txt = Path(fn).read_text() lines = [t.replace("@", " ").split() for t in txt.splitlines()] return [tuple(int(x.strip(",")) for x in a) for a in lines] px, py, pz, dx, dy, dz = sympy.symbols("px, py, pz, dx, dy, dz", integer=True) vals = read("24data.txt") eqs = [] for pxi, pyi, pzi, dxi, dyi, dzi in vals[:3]: eqs.append((pxi - px) * (dy - dyi) - (pyi - py) * (dx - dxi)) eqs.append((pyi - py) * (dz - dzi) - (pzi - pz) * (dy - dyi)) answer = sympy.solve(eqs) print(answer) This is the whole Python solution Resorted to tricks to keep Rust solution manageable
  • 80. Thoughts on Rust Generally very positive! • Loved the developer experience with Cargo • Maxed out clippy (linter) with nursery lints and more • Good support for functional programming was great • Loved the Trait system • Code wasn’t concise, but clear and fun to write • Had to f ight Rust occasionally, but code was better for it • Had zero segfaults. Zero.
  • 81. Thoughts on Rust A few downsides • Code was a lot longer than the Python version • Code sometimes slower than the Python version (higher level libs) • But found pretty good libs with ports of the Python algorithms sometimes • Much younger ecosystem than Python (but there _is_ one, unlike C++/C) • Dependencies are “normal”, so can’t just use stdlib (good and bad) • Some missing features, but it was easy to work around • No generators or ternaries, for example
  • 82. When would I use Rust? Just some ideas • For command line / developer tools • Startup speed is unbelievable compared to any interpreter • Options exist for unicode handling (unlike, say, C++…) • For Python extensions (Python integration is top-notch) • For things that don’t need CUDA • When targeting WebAssembly • If not needing libraries in other languages (though it has cross-compat)
  • 83. Rust compared to other languages A few, anyway • You can write a library that depends on another library! (C++) • Editions can work together (C++) • No/little bad legacy code (like templates without constraints) (C++) • Lazy functional, multiline closures, Traits, and explicit errors (Python) • First-class support for features and pro f iles (C++ or Python)
  • 85. More info? Join the RSE Rust learning group!
  • 86. Bonus: Day 22 Python - Blender viz import bpy import bmesh from mathutils import Matrix import numpy as np TXT = """ 1,0,1~1,2,1 0,0,2~2,0,2 0,2,3~2,2,3 0,0,4~0,2,4 2,0,5~2,2,5 0,1,6~2,1,6 1,1,8~1,1,9""" def add_object(bm, start, end): scale = np.abs(start - end) + 1 matrix = Matrix.LocRotScale((start + end) / 2, None, scale) bmesh.ops.create_cube(bm, size=1.0, matrix=matrix) bm = bmesh.new() for line in TXT.splitlines(): ax, ay, az, bx, by, bz = map(float, line.replace("~", ",").split(",")) add_object(bm, np.array((ax, ay, az)), np.array((bx, by, bz))) me = bpy.data.meshes.new("Mesh") bm.to_mesh(me) bm.free() obj = bpy.data.objects.new("Object", me) bpy.context.collection.objects.link(obj)