commit e96fd9165775ef6971c8579d205c741cb53ff6f0 Author: Christian Nieves Date: Mon Dec 26 14:27:56 2022 -0600 Populate diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8f06baf --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "comprehensive_rust" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..bb51425 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "comprehensive_rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[[bin]] +name = "move-semantics" +path = "src/move-semantics.rs" + +[[bin]] +name = "moves-in-function-calls" +path = "src/moves-in-function-calls.rs" + +[[bin]] +name = "copying" +path = "src/copying.rs" + +[[bin]] +name = "lifetimes-function-calls" +path = "src/lifetimes-function-calls.rs" + +[[bin]] +name = "lifetimes-structs" +path = "src/lifetimes-structs.rs" + +[[bin]] +name = "library" +path = "src/exercises/library.rs" diff --git a/src/day1/copying.rs b/src/day1/copying.rs new file mode 100644 index 0000000..cc0edf1 --- /dev/null +++ b/src/day1/copying.rs @@ -0,0 +1,17 @@ +// https://google.github.io/comprehensive-rust/ownership/copy-clone.html +#[derive(Copy, Clone, Debug)] +struct Point(i32, i32); + +fn main() { + // Copied by default + let x = 42; + let y = x; + println!("x: {x}"); + println!("y: {y}"); + + // implements the Copy trait + let p1 = Point(3, 4); + let p2 = p1; + println!("p1: {p1:?}"); + println!("p2: {p2:?}"); +} diff --git a/src/day1/exercises/iterators-ownership.rs b/src/day1/exercises/iterators-ownership.rs new file mode 100644 index 0000000..9d73257 --- /dev/null +++ b/src/day1/exercises/iterators-ownership.rs @@ -0,0 +1,13 @@ +// https://google.github.io/comprehensive-rust/exercises/day-1/iterators-and-ownership.html + + +// Traits are like interfaces: they describe behavior (methods) for a type. The Iterator trait +// simply says that you can call next until you get None back: +// pub trait Iterator { +// type Item; +// fn next(&mut self) -> Option; +// } + +fn main() { + +} diff --git a/src/day1/exercises/library.rs b/src/day1/exercises/library.rs new file mode 100644 index 0000000..aae6c93 --- /dev/null +++ b/src/day1/exercises/library.rs @@ -0,0 +1,87 @@ +// https://google.github.io/comprehensive-rust/exercises/day-1/book-library.html +struct Library { + books: Vec, +} + +#[derive(Clone, Debug)] +struct Book { + title: String, + year: u16, +} + +impl Book { + // This is a constructor, used below. + fn new(title: &str, year: u16) -> Book { + Book { + title: String::from(title), + year, + } + } +} + +// This makes it possible to print Book values with {}. +impl std::fmt::Display for Book { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} ({})", self.title, self.year) + } +} + +impl Library { + fn new() -> Library { + Library { + books: vec![] + } + } + + fn len(self) -> usize { + self.books.len() + } + + fn is_empty(&self) -> bool { + self.books.len() == 0 + } + + fn add_book(&mut self, book: Book) { + self.books.push(book) + } + + fn print_books(&self) { + for book in self.books.iter() { + println!("book: {book}"); + } + } + + fn oldest_book(&self) -> Option<&Book> { + let mut oldest : Option<&Book> = None; + for book in self.books.iter() { + if oldest.is_none() { + oldest = Option::from(book) + } else if book.year < oldest.unwrap().year { + oldest = Option::from(book) + } + } + + oldest + } +} + +fn main() { + // This shows the desired behavior. Uncomment the code below and + // implement the missing methods. You will need to update the + // method signatures, including the "self" parameter! + let mut library = Library::new(); + + println!("Our library is empty: {}", library.is_empty()); + + library.add_book(Book::new("Lord of the Rings", 1954)); + library.add_book(Book::new("Alice's Adventures in Wonderland", 1865)); + + library.print_books(); + + match library.oldest_book() { + Some(book) => println!("My oldest book is {book}"), + None => println!("My library is empty!"), + } + + println!("Our library has {} books", library.len()); +} diff --git a/src/day1/lifetimes-function-calls.rs b/src/day1/lifetimes-function-calls.rs new file mode 100644 index 0000000..226bbfb --- /dev/null +++ b/src/day1/lifetimes-function-calls.rs @@ -0,0 +1,18 @@ +// https://google.github.io/comprehensive-rust/ownership/lifetimes-function-calls.html +#[derive(Debug)] +struct Point(i32, i32); + +// 'a is a generic parameter, it is inferred by the compiler. +// Lifetimes start with ' and 'a is a typical default name. +// Read &'a Point as “a borrowed Point which is valid for at least the lifetime a”. +// The at least part is important when parameters are in different scopes. +fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point { + if p1.0 < p2.0 { p1 } else { p2 } +} + +fn main() { + let p1: Point = Point(10, 10); + let p2: Point = Point(20, 20); // Put into different scope + let p3: &Point = left_most(&p1, &p2); + println!("left-most point: {:?}", p3); +} diff --git a/src/day1/lifetimes-structs.rs b/src/day1/lifetimes-structs.rs new file mode 100644 index 0000000..1c44c07 --- /dev/null +++ b/src/day1/lifetimes-structs.rs @@ -0,0 +1,18 @@ +// https://google.github.io/comprehensive-rust/ownership/lifetimes-data-structures.html +// +// If a data type stores borrowed data, it must be annotated with a lifetime: +#[derive(Debug)] +struct Highlight<'doc>(&'doc str); + +fn erase(text: String) { + println!("Bye {text}!"); +} + +fn main() { + let text = String::from("The quick brown fox jumps over the lazy dog."); + let fox = Highlight(&text[4..19]); + let dog = Highlight(&text[35..43]); + // erase(text); + println!("{fox:?}"); + println!("{dog:?}"); +} diff --git a/src/day1/move-semantics.rs b/src/day1/move-semantics.rs new file mode 100644 index 0000000..9a8aa5b --- /dev/null +++ b/src/day1/move-semantics.rs @@ -0,0 +1,7 @@ +// https://google.github.io/comprehensive-rust/ownership/move-semantics.html +fn main() { + let s1: String = String::from("Hello!"); + let s2: String = s1; + println!("s2: {s2}"); + // println!("s1: {s1}"); +} diff --git a/src/day1/moves-in-functions-calls.rs b/src/day1/moves-in-functions-calls.rs new file mode 100644 index 0000000..d000d0a --- /dev/null +++ b/src/day1/moves-in-functions-calls.rs @@ -0,0 +1,10 @@ +// https://google.github.io/comprehensive-rust/ownership/moves-function-calls.html +fn say_hello(name: String) { + println!("Hello {name}") +} + +fn main() { + let name = String::from("Alice"); + say_hello(name); + // say_hello(name); +} diff --git a/src/day2/destructure-arrays.rs b/src/day2/destructure-arrays.rs new file mode 100644 index 0000000..02b50dc --- /dev/null +++ b/src/day2/destructure-arrays.rs @@ -0,0 +1,10 @@ +#[rustfmt::skip] +fn main() { + let triple = [0, -2, 3]; + println!("Tell me about {triple:?}"); + match triple { + [0, y, z] => println!("First is 0, y = {y}, and z = {z}"), + [1, ..] => println!("First is 1 and the rest were ignored"), + _ => println!("All elements were ignored"), + } +} diff --git a/src/day2/destructure-enum.rs b/src/day2/destructure-enum.rs new file mode 100644 index 0000000..e4c61c0 --- /dev/null +++ b/src/day2/destructure-enum.rs @@ -0,0 +1,26 @@ +enum Result { + Ok(i32), + Err(String), +} + +fn divide_in_two(n: i32) -> Result { + if n % 2 == 0 { + Result::Ok(n / 2) + } else { + Result::Err(format!("cannot divide {} into two equal parts", n)) + } +} + +fn main() { + let mut n = 100; + match divide_in_two(n) { + Result::Ok(half) => println!("{n} divided in two is {half}"), + Result::Err(msg) => println!("sorry, an error happened: {msg}"), + } + + n = 105; + match divide_in_two(n) { + Result::Ok(half) => println!("{n} divided in two is {half}"), + Result::Err(msg) => println!("sorry, an error happened: {msg}"), + } +} diff --git a/src/day2/destructure-struct.rs b/src/day2/destructure-struct.rs new file mode 100644 index 0000000..7d4deb2 --- /dev/null +++ b/src/day2/destructure-struct.rs @@ -0,0 +1,14 @@ +struct Foo { + x: (u32, u32), + y: u32, +} + +#[rustfmt::skip] +fn main() { + let foo = Foo { x: (1, 2), y: 3 }; + match foo { + Foo { x: (1, b), y } => println!("x.0 = 1, b = {b}, y = {y}"), + Foo { y: 2, x: i } => println!("y = 2, i = {i:?}"), + Foo { y, .. } => println!("y = {y}, other fields were ignored"), + } +} diff --git a/src/day2/enum_sizes.rs b/src/day2/enum_sizes.rs new file mode 100644 index 0000000..034af6b --- /dev/null +++ b/src/day2/enum_sizes.rs @@ -0,0 +1,18 @@ +use std::mem::{align_of, size_of}; + +macro_rules! dbg_size { + ($t:ty) => { + println!("{}: size {} bytes, align: {} bytes", + stringify!($t), size_of::<$t>(), align_of::<$t>()); + }; +} + +enum Foo { + A, + B, +} + +fn main() { + // adding a variang-payload will change the size :) + dbg_size!(Foo); +} diff --git a/src/day2/enums.rs b/src/day2/enums.rs new file mode 100644 index 0000000..06a5163 --- /dev/null +++ b/src/day2/enums.rs @@ -0,0 +1,22 @@ +fn generate_random_number() -> i32 { + 4 // Chosen by fair dice roll. Guaranteed to be random. +} + +#[derive(Debug)] +enum CoinFlip { + Heads, + Tails, +} + +fn flip_coin() -> CoinFlip { + let random_number = generate_random_number(); + if random_number % 2 == 0 { + return CoinFlip::Heads; + } else { + return CoinFlip::Tails; + } +} + +fn main() { + println!("You got: {:?}", flip_coin()); +} diff --git a/src/day2/field-shorthand.rs b/src/day2/field-shorthand.rs new file mode 100644 index 0000000..2c2309b --- /dev/null +++ b/src/day2/field-shorthand.rs @@ -0,0 +1,23 @@ +// https://google.github.io/comprehensive-rust/structs/field-shorthand.html +// If you already have variables with the right names, then you can create the struct using a shorthand: +#[derive(Debug)] +struct Person { + name: String, + age: u8, +} + +impl Person { + fn new(name: String, age: u8) -> Person { + Person { name, age } + } + + // This WONT compile: + // fn newBad(nameBad: String, ageBad: u8) -> Person { + // Person { nameBad, ageBad } + // } +} + +fn main() { + let peter = Person::new(String::from("Peter"), 27); + println!("{peter:?}"); +} diff --git a/src/day2/match-guards.rs b/src/day2/match-guards.rs new file mode 100644 index 0000000..f725057 --- /dev/null +++ b/src/day2/match-guards.rs @@ -0,0 +1,13 @@ +// When matching, you can add a guard to a pattern. This is an arbitrary Boolean expression which will be executed if the pattern matches: + +#[rustfmt::skip] +fn main() { + let pair = (2, -2); + println!("Tell me about {pair:?}"); + match pair { + (x, y) if x == y => println!("These are twins"), + (x, y) if x + y == 0 => println!("Antimatter, kaboom!"), + (x, _) if x % 2 == 1 => println!("The first one is odd"), + _ => println!("No correlation..."), + } +} diff --git a/src/day2/methods.rs b/src/day2/methods.rs new file mode 100644 index 0000000..92ba859 --- /dev/null +++ b/src/day2/methods.rs @@ -0,0 +1,19 @@ +#[derive(Debug)] +struct Person { + name : String, + age: u8, +} + +impl Person { + fn say_hello(&self) { + println!("Hello my name is {}", self.name) + } +} + +fn main() { + let christian = Person { + name : String::from("Christian"), + age: 25, + }; + christian.say_hello() +} diff --git a/src/day2/pattern-matching.rs b/src/day2/pattern-matching.rs new file mode 100644 index 0000000..1db0d73 --- /dev/null +++ b/src/day2/pattern-matching.rs @@ -0,0 +1,11 @@ +fn main() { + let input = 'x'; + + match input { + 'q' => println!("Quitting"), + 'a' | 's' | 'w' | 'd' => println!("Moving around"), + '0'..='9' => println!("Number input"), + _ => println!("Something else"), + } +} + diff --git a/src/day2/receivers.rs b/src/day2/receivers.rs new file mode 100644 index 0000000..ff7dbe0 --- /dev/null +++ b/src/day2/receivers.rs @@ -0,0 +1,50 @@ +// https://google.github.io/comprehensive-rust/methods/example.html +// +// The &self below indicates that the method borrows the object immutably. There are other possible receivers for a method: +// +// * &self: borrows the object from the caller using a shared and immutable reference. The object can be used again afterwards. +// * &mut self: borrows the object from the caller using a unique and mutable reference. The object can be used again afterwards. +// * self: takes ownership of the object and moves it away from the caller. The method becomes the owner of the object. The object will be drop (deallocated) when the method returns, unless it’s ownership is explicitly transmitted. +// * No receiver: this becomes a static method on the struct. Typically used to create constructors which are called new by convention. + +#[derive(Debug)] +struct Race { + name: String, + laps: Vec, +} + +impl Race { + fn new(name: &str) -> Race { // No receiver, a static method + Race { name: String::from(name), laps: Vec::new() } + } + + fn add_lap(&mut self, lap: i32) { // Exclusive borrowed read-write access to self + self.laps.push(lap); + } + + fn print_laps(&self) { // Shared and read-only borrowed access to self + println!("Recorded {} laps for {}:", self.laps.len(), self.name); + for (idx, lap) in self.laps.iter().enumerate() { + println!("Lap {idx}: {lap} sec"); + } + } + + fn finish(self) { // Exclusive ownership of self + let total = self.laps.iter().sum::(); + println!("Race {} is finished, total lap time: {}", self.name, total); + } +} + +fn main() { + let mut race = Race::new("Monaco Grand Prix"); + race.add_lap(70); + race.add_lap(68); + race.print_laps(); + race.add_lap(71); + race.print_laps(); + race.finish(); + + // using `race` after here is invalid since ownership was moved to the finish method + // + // race.print_laps() +} diff --git a/src/day2/structs.rs b/src/day2/structs.rs new file mode 100644 index 0000000..7d0ffa9 --- /dev/null +++ b/src/day2/structs.rs @@ -0,0 +1,15 @@ +// https://google.github.io/comprehensive-rust/structs.html + +struct Person { + name: String, + age: u8, +} + +fn main() { + let peter = Person { + name: String::from("Christian"), + age: 25, + }; + + println!("{} is {} years old", peter.name, peter.age); +} diff --git a/src/day2/tuple-structs.rs b/src/day2/tuple-structs.rs new file mode 100644 index 0000000..6528a0c --- /dev/null +++ b/src/day2/tuple-structs.rs @@ -0,0 +1,19 @@ +// If the field names are unimportant, you can use a tuple struct: +struct Point(i32, i32); + +// This is often used for single-field wrappers (called newtypes). +// Single-field wrappers are similar to type-aliases from Golang. +#[derive(Debug)] +struct Newtons(f64); + +fn compute_thruster_force() -> Newtons { + todo!("Ask a rocket scientist at NASA") +} + +fn main() { + let p = Point(17, 23); + println!("({}, {})", p.0, p.1); + + let force = compute_thruster_force(); + println!("{:?}", force) +} diff --git a/src/day2/variant-payloads.rs b/src/day2/variant-payloads.rs new file mode 100644 index 0000000..597321f --- /dev/null +++ b/src/day2/variant-payloads.rs @@ -0,0 +1,24 @@ +enum WebEvent { + PageLoad, // Variant without payload + KeyPress(char), // Tuple struct variant + Click { x: i64, y: i64 }, // Full struct variant +} + +#[rustfmt::skip] +fn inspect(event: WebEvent) { + match event { + WebEvent::PageLoad => println!("page loaded"), + WebEvent::KeyPress(c) => println!("pressed '{c}'"), + WebEvent::Click { x, y } => println!("clicked at x={x}, y={y}"), + } +} + +fn main() { + let load = WebEvent::PageLoad; + let press = WebEvent::KeyPress('x'); + let click = WebEvent::Click { x: 20, y: 80 }; + + inspect(load); + inspect(press); + inspect(click); +}