Getting Started with Rust
2026-09-01
enum TicketStatus {
Assigned { assigned_to: String },
Done
}
fn count_assignments(tickets: &[TicketStatus], name: &str) -> u32 {
let mut count = 0;
// TODO: Loop over tickets and count how many are assigned to name
count
}
fn main() {
let tickets = [ TicketStatus::Done,
TicketStatus::Assigned{ assigned_to: String::from("dbindel") },
TicketStatus::Assigned{ assigned_to: String::from("raja") },
TicketStatus::Assigned{ assigned_to: String::from("dbindel") }];
println!("dbindel assigned {} tickets", count_assignments(&tickets, "dbindel"));
}Last week (Ch 1-6):
struct and methods, enum and matchngAnd some programming practice on Friday.
There are lots of resources to learn Rust!
Spend some time exploring! I have suggestions.
“If you want to go fast, go alone, if you want to go far, go together”
– Source unknown
Thus Thursday, Sep 3 from 4:30-5:40 in Gates 114.
Organized by Women in Computing at Cornell (WICC).
impl block like other methods
TraitName for StructNamestruct Dog(String);
trait Animal {
fn describe(&self) -> String;
}
impl Animal for Dog {
fn describe(&self) -> String { format!("{} is a dog.", self.0) }
}
trait Pet: Animal { // Must implement Animal to implement Pet
fn greet(&self) -> String;
}
impl Pet for Dog {
fn greet(&self) -> String { format!("Good boy, {}!", self.0) }
}trait LinearMap {
type Input; // Associated type
type Output; // Associated type
fn map(&self, x: &Self::Input) -> Self::Output;
}
struct RotateMap(f64);
impl LinearMap for RotateMap {
type Input = [f64; 2];
type Output = [f64; 2];
fn map(&self, x: &Self::Input) -> Self::Output {
let c = self.0.cos();
let s = self.0.sin();
[c*x[0]-s*x[1], s*x[0]+c*x[1]]
}
}derive macro writes code for youDebug: Generate debug formatterCopy, Clone: implement copying and clonePartialEq: partial equality (why partial?)where separates trait bounds from type declaration+ allows trait bound intersectionuse std::ops::Add;
use std::ops::Mul;
use std::convert::From;
trait ScalarType: Add<Output=Self> + Mul<Output=Self> + From<u8> + Copy {}
impl ScalarType for f64 {}
impl ScalarType for f32 {}
fn dot<T: ScalarType, const N: usize>(x: &[T; N], y: &[T; N]) -> T {
let mut result = T::from(0);
for i in 0..N {
result = result + x[i]*y[i];
}
result
}Can also use supertraits to consolidate trait bounds.
use std::ops::Add;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point2d<T>(T,T);
impl<T: Add<Output=T>> Add for Point2d<T> {
type Output=Self
fn add(self, other: Self) -> Self {
return Point2d<T>(self.0 + other.0, self.1 + other.1);
}
}
fn main() {
let p = Point2d(1,2);
let q = Point2d(3,4);
println!("{:?}", p+q);
}We have already been using generic traits!
String::from("foo"), T::from(0)Sized lateruse std::cmp::Ord; // Method cmp(&self, &x) -> Ordering
use std::cmp::Ordering; // enum Ordering { Less, Equal, Greater }
fn bubblesort<...>(v: ...) { // TODO: What should the types be?
for _ in 0..v.len() {
for i in 1..v.len() {
// TODO: Call v.swap(i-1, i) if v[i-1] > v[i]
}
}
}
fn main() {
let mut x = vec![8, 6, 7, 5, 3, 0, 9];
bubblesort(&mut x);
println!("{x:?}")
}Call panic!() for unrecoverable errors
assert! failure, out of bound access, OOMFor recoverable errors, use a Result
Use Result for recoverable errors
unwrap() or expect(msg) methods match Ok or panicis_ok or is_errmatch, if let, let else constructsor_else(f) - Calls handler on Errunwrap_or(default) - Returns default on Errunwrap_or_else(f) - like or_else, but unwrapsOptionMore on these after we cover closures.
use std::fs::File;
use std::io::Read;
fn read_file(fname: &str) -> Result<String, std::io::Error> {
let mut contents = String::new();
File::open(fname)?.read_to_string(&mut contents)?;
Ok(contents)
}
fn main() {
let Ok(s) = read_file("input.txt") else {
println!("Could not read file");
return;
};
println!("{}", s);
}Try operator (?) propagates errors (unwrap Ok or return error)
Actual behavior of try:
Useful for when there are multiple error types.
#[derive(Debug)]
enum MyError { IoError(std::io::Error), EmptyUserError }
impl Error for MyError {}
impl From<std::io::Error> for MyError {
fn from(err: std::io::Error) -> Self { Self::IoError(err) }
}
fn read_username(path: &str) -> Result<String, MyError> {
let mut username = String::new();
std::io::fs::File::open(path)?.read_to_string(&mut username)?;
if username.is_empty() {
return Err(MyError::EmptyUserError);
};
Ok(username)
}The thiserror crate provides an Error derive macro and various other utilities.
What is the difference between Option and Result?
None carries no error informationuse thiserror::Error;
struct User { id: String, name: String }
#[derive(Debug, Error)]
enum UserError {
#[error("IO error: {0}")]
IoError(std::io::Error),
#[error("Missing username: {0}")]
MissingError(String),
}
fn read_users(path: &str) -> ... { // TODO: Fill in the type
// Implementation elided, but return Vec<User> or an I/O error
}
fn lookup_name(path: &str, id: &str) -> ... { // TODO: Fill in the type
// TODO: Call read_users to get data and look up name (if id present)
}lib.rs or main.rsboots::cats says to look in src/boots/cats.rssuper points to parent moduleself points to current moduleuse brings a name into current space (pub use re-exports)std structs and traitsSelected associated functions:
new(): Create new empty Vecwith_capacity(c): Create empty Vec with capacity cSelected methods:
push(v) and pop(): Stack operationsinsert(i,v) and remove(i): Indexed insert/removelen() and capacity(): Length and capacitypush a character or push_str a stringlen and capacity are in bytesnth(i) for character i, index by byteschars for iterator over charactersinsert(k,v) - Insert a keyget(k) - Get value (returns Option)entry(k) - Get an EntryIn std::cmp
PartialEq<RHS> for partial equivalence (== and !=)Eq: PartialEq if PartialEq actually an equivalencePartialOrd: PartialEq for partial orderingOrd: Eq + PartialOrd for complete orderingEqIn std::ops
Add, Sub, Mul, Div for implementing +, -, *, /AddAssign, etc for += and the likeIndex and IndexMut to overload subscriptingFn, FnMut, and FnOnce to overload calling
In std::convert
From<T> for U says how to convert a T to a UInto<T> for U says how to convert a U into a TFrom<U> for T implies Into<T> for UTryFrom and TryInto if failure is possibleIn std::io
Read trait implements read() to read bytesWrite trait implements write() to write bytesYes! Six lectures is only about 7.5 hours!
You learn by doing, so we have a first project:
listen and silentCheck-in due next Monday, project due in two weeks.
Tell me either:
Comments on course sheets
enumandstructin practice today