This commit is contained in:
Christian Nieves
2022-12-26 14:27:56 -06:00
commit e96fd91657
23 changed files with 474 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View File

@ -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"

32
Cargo.toml Normal file
View File

@ -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"

17
src/day1/copying.rs Normal file
View File

@ -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:?}");
}

View File

@ -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<Self::Item>;
// }
fn main() {
}

View File

@ -0,0 +1,87 @@
// https://google.github.io/comprehensive-rust/exercises/day-1/book-library.html
struct Library {
books: Vec<Book>,
}
#[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());
}

View File

@ -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);
}

View File

@ -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:?}");
}

View File

@ -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}");
}

View File

@ -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);
}

View File

@ -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"),
}
}

View File

@ -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}"),
}
}

View File

@ -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"),
}
}

18
src/day2/enum_sizes.rs Normal file
View File

@ -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);
}

22
src/day2/enums.rs Normal file
View File

@ -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());
}

View File

@ -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:?}");
}

13
src/day2/match-guards.rs Normal file
View File

@ -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..."),
}
}

19
src/day2/methods.rs Normal file
View File

@ -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()
}

View File

@ -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"),
}
}

50
src/day2/receivers.rs Normal file
View File

@ -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 its 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<i32>,
}
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::<i32>();
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()
}

15
src/day2/structs.rs Normal file
View File

@ -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);
}

19
src/day2/tuple-structs.rs Normal file
View File

@ -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)
}

View File

@ -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);
}