Compare commits

..

12 Commits

Author SHA1 Message Date
LukeMathWalker
a6056381bd No need to deploy anymore. 2024-07-30 16:13:20 +02:00
code-cp
59833f2a55 Update 06_async_aware_primitives.md (#122)
Fix a typo
2024-07-28 12:46:20 +02:00
Zhang Zihao
9a2086081c Fix a typo (#116) 2024-07-17 08:08:22 +02:00
Jack Moffitt
f272843c61 Remove pub visibility on server() as the argument has a private type. This gets rid of a warning. (#112) 2024-07-07 21:18:43 +02:00
Evgeniy Filimonov
fccad08921 07_threads: 03_leak: Leak vector with Vec::leak, not Box::leak (#107) 2024-06-30 18:23:20 +02:00
Palash Nigam (He/Him)
de45f8adf2 Ch-08 Futures Exercise 02: Fix typo (#106) 2024-06-30 00:18:46 +02:00
LOGI
5660a2f7a8 fix(typo): a module name in comments (#102)
The output of the compiler does not include the module name of the `Ticket` struct and the root module of this exercise is `visibility` rather than `encapsulation` which is the root module of the next exercise.
2024-06-27 11:35:11 +02:00
Saqib Ahmed
491319a6d5 fix: fix a typo (#103) 2024-06-27 11:34:02 +02:00
Jerry Wu
83cf1cad62 Update 11_locks.md (#94)
Suggest removing an extra semicolon.
2024-06-20 10:21:53 +02:00
Ernie Hershey
d8d7e73f1c fix syntax with comma (#89)
Example doesn't compile with a comma here
2024-06-20 10:21:33 +02:00
Onè
468de3c0ac Change test to require impl (#87)
impl std::ops::Add<&SaturatingU16> for SaturatingU16
2024-06-20 10:21:14 +02:00
tomgrbz
c86360f3c4 Remove array/slice syntax from argument &mut str in TODO comment for lowercase func (#99)
Co-authored-by: thomasgrbic <grbic.t@northeastern.edu>
2024-06-20 10:18:55 +02:00
43 changed files with 79 additions and 266 deletions

View File

@@ -39,18 +39,6 @@ jobs:
with: with:
name: book name: book
path: book/book path: book/book
# Commit and push all changed files.
# Must only affect files that are listed in "paths-ignore".
- name: Git commit build artifacts
# Only run on main branch push (e.g. pull request merge).
if: github.event_name == 'push'
run: |
git checkout -b deploy
git config --global user.name "Deployer"
git config --global user.email "username@users.noreply.github.com"
git add --force book/book
git commit -m "Render book"
git push --set-upstream --force-with-lease origin deploy
formatter: formatter:
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@@ -1,6 +1,3 @@
[workspace] [workspace]
members = ["exercises/*/*", "helpers/common", "helpers/mdbook-exercise-linker", "helpers/ticket_fields"] members = ["exercises/*/*", "helpers/common", "helpers/mdbook-exercise-linker", "helpers/ticket_fields"]
resolver = "2" resolver = "2"
[profile.dev]
overflow-checks = false

View File

@@ -108,7 +108,7 @@ let title = String::from("A title");
We've been primarily using `.into()`, though.\ We've been primarily using `.into()`, though.\
If you check out the [implementors of `Into`](https://doc.rust-lang.org/std/convert/trait.Into.html#implementors) If you check out the [implementors of `Into`](https://doc.rust-lang.org/std/convert/trait.Into.html#implementors)
you won't find `Into<&str> for String`. What's going on? you won't find `Into<String> for &str`. What's going on?
`From` and `Into` are **dual traits**.\ `From` and `Into` are **dual traits**.\
In particular, `Into` is implemented for any type that implements `From` using a **blanket implementation**: In particular, `Into` is implemented for any type that implements `From` using a **blanket implementation**:

View File

@@ -8,7 +8,7 @@ impl Ticket {
match &self.status { match &self.status {
Status::InProgress { assigned_to } => assigned_to, Status::InProgress { assigned_to } => assigned_to,
Status::Done | Status::ToDo => { Status::Done | Status::ToDo => {
panic!("Only `In-Progress` tickets can be assigned to someone"), panic!("Only `In-Progress` tickets can be assigned to someone")
} }
} }
} }

View File

@@ -27,12 +27,12 @@ run out and crash with an out-of-memory error.
fn oom_trigger() { fn oom_trigger() {
loop { loop {
let v: Vec<usize> = Vec::with_capacity(1024); let v: Vec<usize> = Vec::with_capacity(1024);
Box::leak(v); v.leak();
} }
} }
``` ```
At the same time, memory leaked via `Box::leak` is not truly forgotten.\ At the same time, memory leaked via `leak` method is not truly forgotten.\
The operating system can map each memory region to the process responsible for it. The operating system can map each memory region to the process responsible for it.
When the process exits, the operating system will reclaim that memory. When the process exits, the operating system will reclaim that memory.

View File

@@ -99,7 +99,7 @@ fn main() {
let guard = lock.lock().unwrap(); let guard = lock.lock().unwrap();
spawn(move || { spawn(move || {
receiver.recv().unwrap();; receiver.recv().unwrap();
}); });
// Try to send the guard over the channel // Try to send the guard over the channel
@@ -118,7 +118,7 @@ error[E0277]: `MutexGuard<'_, i32>` cannot be sent between threads safely
| _-----_^ | _-----_^
| | | | | |
| | required by a bound introduced by this call | | required by a bound introduced by this call
11 | | receiver.recv().unwrap();; 11 | | receiver.recv().unwrap();
12 | | }); 12 | | });
| |_^ `MutexGuard<'_, i32>` cannot be sent between threads safely | |_^ `MutexGuard<'_, i32>` cannot be sent between threads safely
| |

View File

@@ -112,7 +112,7 @@ pub async fn work() {
### `std::thread::spawn` vs `tokio::spawn` ### `std::thread::spawn` vs `tokio::spawn`
You can think of `tokio::spawn` as the asynchronous sibling of `std::spawn::thread`. You can think of `tokio::spawn` as the asynchronous sibling of `std::thread::spawn`.
Notice a key difference: with `std::thread::spawn`, you're delegating control to the OS scheduler. Notice a key difference: with `std::thread::spawn`, you're delegating control to the OS scheduler.
You're not in control of how threads are scheduled. You're not in control of how threads are scheduled.

View File

@@ -52,7 +52,7 @@ Yields to runtime
Tries to acquire lock Tries to acquire lock
``` ```
We have a deadlock. Task B we'll never manage to acquire the lock, because the lock We have a deadlock. Task B will never manage to acquire the lock, because the lock
is currently held by task A, which has yielded to the runtime before releasing the is currently held by task A, which has yielded to the runtime before releasing the
lock and won't be scheduled again because the runtime cannot preempt task B. lock and won't be scheduled again because the runtime cannot preempt task B.

View File

@@ -17,7 +17,7 @@
// You can also find solutions to all exercises in the `solutions` git branch. // You can also find solutions to all exercises in the `solutions` git branch.
fn greeting() -> &'static str { fn greeting() -> &'static str {
// TODO: fix me 👇 // TODO: fix me 👇
"I'm ready to learn Rust!" "I'm ready to __!"
} }
// Your solutions will be automatically verified by a set of tests. // Your solutions will be automatically verified by a set of tests.

View File

@@ -3,7 +3,7 @@
// partner in this course and it'll often guide you in the right direction! // partner in this course and it'll often guide you in the right direction!
// //
// The input parameters should have the same type of the return type. // The input parameters should have the same type of the return type.
fn compute(a: u32, b: u32) -> u32 { fn compute(a, b) -> u32 {
// Don't touch the function body. // Don't touch the function body.
a + b * 2 a + b * 2
} }

View File

@@ -1,6 +1,6 @@
fn intro() -> &'static str { fn intro() -> &'static str {
// TODO: fix me 👇 // TODO: fix me 👇
"I'm ready to build a calculator in Rust!" "I'm ready to __!"
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -1,6 +1,6 @@
fn compute(a: u32, b: u32) -> u32 { fn compute(a: u32, b: u32) -> u32 {
// TODO: change the line below to fix the compiler error and make the tests pass. // TODO: change the line below to fix the compiler error and make the tests pass.
a + b * 4u32 a + b * 4u8
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -8,7 +8,7 @@
pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 { pub fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: define a variable named `distance` with the right value to get tests to pass // TODO: define a variable named `distance` with the right value to get tests to pass
// Do you need to annotate the type of `distance`? Why or why not? // Do you need to annotate the type of `distance`? Why or why not?
let distance: u32 = end - start;
// Don't change the line below // Don't change the line below
distance / time_elapsed distance / time_elapsed
} }

View File

@@ -1,6 +1,6 @@
/// Return `true` if `n` is even, `false` otherwise. /// Return `true` if `n` is even, `false` otherwise.
fn is_even(n: u32) -> bool { fn is_even(n: u32) -> bool {
n % 2 == 0 todo!()
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -2,9 +2,7 @@
/// calculate the average speed of the journey. /// calculate the average speed of the journey.
fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 { fn speed(start: u32, end: u32, time_elapsed: u32) -> u32 {
// TODO: Panic with a custom message if `time_elapsed` is 0 // TODO: Panic with a custom message if `time_elapsed` is 0
if time_elapsed == 0 {
panic!("The journey took no time at all, that's impossible!");
}
(end - start) / time_elapsed (end - start) / time_elapsed
} }

View File

@@ -9,13 +9,6 @@
// `factorial(2)` to return `2`, and so on. // `factorial(2)` to return `2`, and so on.
// //
// Use only what you learned! No loops yet, so you'll have to use recursion! // Use only what you learned! No loops yet, so you'll have to use recursion!
fn factorial(n: u16) -> u16 {
if n == 0 {
1
} else {
n * factorial(n - 1)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@@ -4,13 +4,7 @@ pub fn factorial(n: u32) -> u32 {
// interprets as "I'll get back to this later", thus // interprets as "I'll get back to this later", thus
// suppressing type errors. // suppressing type errors.
// It panics at runtime. // It panics at runtime.
let mut result: u32 = 1; // base case todo!()
let mut i: u32 = 1;
while i <= n {
result *= i;
i += 1;
}
result
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -1,10 +1,6 @@
// Rewrite the factorial function using a `for` loop. // Rewrite the factorial function using a `for` loop.
pub fn factorial(n: u32) -> u32 { pub fn factorial(n: u32) -> u32 {
let mut result: u32 = 1; // base case todo!()
for i in 2..=n {
result *= i;
}
result
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -1,9 +1,9 @@
pub fn factorial(n: u32) -> u32 { pub fn factorial(n: u32) -> u32 {
let mut result: u32 = 1; let mut result = 1;
for i in 1..=n { for i in 1..=n {
// Use saturating multiplication to stop at the maximum value of u32 // Use saturating multiplication to stop at the maximum value of u32
// rather than overflowing and wrapping around // rather than overflowing and wrapping around
result = result.saturating_mul(i); result *= i;
} }
result result
} }

View File

@@ -6,7 +6,7 @@ mod tests {
#[test] #[test]
fn u16_to_u32() { fn u16_to_u32() {
let v: u32 = 47; let v: u32 = todo!();
assert_eq!(47u16 as u32, v); assert_eq!(47u16 as u32, v);
} }
@@ -24,14 +24,14 @@ mod tests {
// You could solve this by using exactly the same expression as above, // You could solve this by using exactly the same expression as above,
// but that would defeat the purpose of the exercise. Instead, use a genuine // but that would defeat the purpose of the exercise. Instead, use a genuine
// `i8` value that is equivalent to `255` when converted from `u8`. // `i8` value that is equivalent to `255` when converted from `u8`.
let y: i8 = -1; let y: i8 = todo!();
assert_eq!(x, y); assert_eq!(x, y);
} }
#[test] #[test]
fn bool_to_u8() { fn bool_to_u8() {
let v: u8 = 1; let v: u8 = todo!();
assert_eq!(true as u8, v); assert_eq!(true as u8, v);
} }
} }

View File

@@ -1,6 +1,6 @@
fn intro() -> &'static str { fn intro() -> &'static str {
// TODO: fix me 👇 // TODO: fix me 👇
"I'm ready to start modelling a software ticket!" "I'm ready to __!"
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -4,16 +4,6 @@
// //
// It should also have a method named `is_available` that returns a `true` if the quantity is // It should also have a method named `is_available` that returns a `true` if the quantity is
// greater than 0, otherwise `false`. // greater than 0, otherwise `false`.
struct Order {
price: u8,
quantity: u8,
}
impl Order {
fn is_available(self) -> bool {
self.quantity > 0
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@@ -17,22 +17,7 @@ impl Ticket {
// as well as some `String` methods. Use the documentation of Rust's standard library // as well as some `String` methods. Use the documentation of Rust's standard library
// to find the most appropriate options -> https://doc.rust-lang.org/std/string/struct.String.html // to find the most appropriate options -> https://doc.rust-lang.org/std/string/struct.String.html
fn new(title: String, description: String, status: String) -> Self { fn new(title: String, description: String, status: String) -> Self {
if status != "To-Do" && status != "In Progress" && status != "Done" { todo!();
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed")
}
if title.is_empty() {
panic!("Title cannot be empty")
}
if description.is_empty() {
panic!("Description cannot be empty")
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes")
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes")
}
Self { Self {
title, title,
description, description,

View File

@@ -1,7 +1,6 @@
mod helpers { mod helpers {
// TODO: Make this code compile, either by adding a `use` statement or by using // TODO: Make this code compile, either by adding a `use` statement or by using
// the appropriate path to refer to the `Ticket` struct. // the appropriate path to refer to the `Ticket` struct.
use super::Ticket;
fn create_todo_ticket(title: String, description: String) -> Ticket { fn create_todo_ticket(title: String, description: String) -> Ticket {
Ticket::new(title, description, "To-Do".into()) Ticket::new(title, description, "To-Do".into())

View File

@@ -1,12 +1,12 @@
mod ticket { mod ticket {
pub struct Ticket { struct Ticket {
title: String, title: String,
description: String, description: String,
status: String, status: String,
} }
impl Ticket { impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket { fn new(title: String, description: String, status: String) -> Ticket {
if title.is_empty() { if title.is_empty() {
panic!("Title cannot be empty"); panic!("Title cannot be empty");
} }
@@ -48,14 +48,14 @@ mod tests {
// You should be seeing this error when trying to run this exercise: // You should be seeing this error when trying to run this exercise:
// //
// error[E0616]: field `description` of struct `encapsulation::ticket::Ticket` is private // error[E0616]: field `description` of struct `Ticket` is private
// | // |
// | assert_eq!(ticket.description, "A description"); // | assert_eq!(ticket.description, "A description");
// | ^^^^^^^^^^^^^^^^^^ // | ^^^^^^^^^^^^^^^^^^
// //
// TODO: Once you have verified that the below does not compile, // TODO: Once you have verified that the below does not compile,
// comment the line out to move on to the next exercise! // comment the line out to move on to the next exercise!
// assert_eq!(ticket.description, "A description"); assert_eq!(ticket.description, "A description");
} }
fn encapsulation_cannot_be_violated() { fn encapsulation_cannot_be_violated() {
@@ -68,10 +68,10 @@ mod tests {
// //
// TODO: Once you have verified that the below does not compile, // TODO: Once you have verified that the below does not compile,
// comment the lines out to move on to the next exercise! // comment the lines out to move on to the next exercise!
// let ticket = Ticket { let ticket = Ticket {
// title: "A title".into(), title: "A title".into(),
// description: "A description".into(), description: "A description".into(),
// status: "To-Do".into(), status: "To-Do".into(),
// }; };
} }
} }

View File

@@ -34,17 +34,6 @@ pub mod ticket {
// - `title` that returns the `title` field. // - `title` that returns the `title` field.
// - `description` that returns the `description` field. // - `description` that returns the `description` field.
// - `status` that returns the `status` field. // - `status` that returns the `status` field.
pub fn title(self) -> String {
self.title
}
pub fn description(self) -> String {
self.description
}
pub fn status(self) -> String {
self.status
}
} }
} }

View File

@@ -34,16 +34,16 @@ impl Ticket {
} }
} }
pub fn title(&self) -> &String { pub fn title(self) -> String {
&self.title self.title
} }
pub fn description(&self) -> &String { pub fn description(self) -> String {
&self.description self.description
} }
pub fn status(&self) -> &String { pub fn status(self) -> String {
&self.status self.status
} }
} }

View File

@@ -11,9 +11,21 @@ pub struct Ticket {
impl Ticket { impl Ticket {
pub fn new(title: String, description: String, status: String) -> Ticket { pub fn new(title: String, description: String, status: String) -> Ticket {
validate_title(&title); if title.is_empty() {
validate_description(&description); panic!("Title cannot be empty");
validate_status(&status); }
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
Ticket { Ticket {
title, title,
@@ -33,45 +45,6 @@ impl Ticket {
pub fn status(&self) -> &String { pub fn status(&self) -> &String {
&self.status &self.status
} }
pub fn set_title(&mut self, title: String) {
validate_title(&title);
self.title = title;
}
pub fn set_description(&mut self, description: String) {
validate_description(&description);
self.description = description;
}
pub fn set_status(&mut self, status: String) {
validate_status(&status);
self.status = status;
}
}
fn validate_title(title: &String) {
if title.is_empty() {
panic!("Title cannot be empty");
}
if title.len() > 50 {
panic!("Title cannot be longer than 50 bytes");
}
}
fn validate_description(description: &String) {
if description.is_empty() {
panic!("Description cannot be empty");
}
if description.len() > 500 {
panic!("Description cannot be longer than 500 bytes");
}
}
fn validate_status(status: &String) {
if status != "To-Do" && status != "In Progress" && status != "Done" {
panic!("Only `To-Do`, `In Progress`, and `Done` statuses are allowed");
}
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -6,16 +6,16 @@ mod tests {
#[test] #[test]
fn u16_size() { fn u16_size() {
assert_eq!(size_of::<u16>(), 2); assert_eq!(size_of::<u16>(), todo!());
} }
#[test] #[test]
fn i32_size() { fn i32_size() {
assert_eq!(size_of::<i32>(), 4); assert_eq!(size_of::<i32>(), todo!());
} }
#[test] #[test]
fn bool_size() { fn bool_size() {
assert_eq!(size_of::<bool>(), 1); assert_eq!(size_of::<bool>(), todo!());
} }
} }

View File

@@ -13,7 +13,7 @@ mod tests {
#[test] #[test]
fn string_size() { fn string_size() {
assert_eq!(size_of::<String>(), 24); assert_eq!(size_of::<String>(), todo!());
} }
#[test] #[test]
@@ -23,6 +23,6 @@ mod tests {
// but, in general, the memory layout of structs is a more complex topic. // but, in general, the memory layout of structs is a more complex topic.
// If you're curious, check out the "Data layout" section of the Rustonomicon // If you're curious, check out the "Data layout" section of the Rustonomicon
// https://doc.rust-lang.org/nomicon/data.html for more information. // https://doc.rust-lang.org/nomicon/data.html for more information.
assert_eq!(size_of::<Ticket>(), 72); assert_eq!(size_of::<Ticket>(), todo!());
} }
} }

View File

@@ -13,16 +13,16 @@ mod tests {
#[test] #[test]
fn u16_ref_size() { fn u16_ref_size() {
assert_eq!(size_of::<&u16>(), 8); assert_eq!(size_of::<&u16>(), todo!());
} }
#[test] #[test]
fn u64_mut_ref_size() { fn u64_mut_ref_size() {
assert_eq!(size_of::<&mut u64>(), 8); assert_eq!(size_of::<&mut u64>(), todo!());
} }
#[test] #[test]
fn ticket_ref_size() { fn ticket_ref_size() {
assert_eq!(size_of::<&Ticket>(), 8); assert_eq!(size_of::<&Ticket>(), todo!());
} }
} }

View File

@@ -2,7 +2,7 @@
// We'll pick the concept up again in a later chapter after covering traits and // We'll pick the concept up again in a later chapter after covering traits and
// interior mutability. // interior mutability.
fn outro() -> &'static str { fn outro() -> &'static str {
"I have a basic understanding of destructors!" "I have a basic understanding of __!"
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -11,74 +11,3 @@
// Integration here has a very specific meaning: they test **the public API** of your project. // Integration here has a very specific meaning: they test **the public API** of your project.
// You'll need to pay attention to the visibility of your types and methods; integration // You'll need to pay attention to the visibility of your types and methods; integration
// tests can't access private or `pub(crate)` items. // tests can't access private or `pub(crate)` items.
pub struct Order {
product_name: String,
quantity: u32,
unit_price: u32,
}
impl Order {
pub fn new(product_name: String, quantity: u32, unit_price: u32) -> Order {
validate_product_name(&product_name);
validate_quantity(&quantity);
validate_unit_price(&unit_price);
Order {
product_name,
quantity,
unit_price,
}
}
pub fn product_name(&self) -> &String {
&self.product_name
}
pub fn quantity(&self) -> &u32 {
&self.quantity
}
pub fn unit_price(&self) -> &u32 {
&self.unit_price
}
pub fn set_product_name(&mut self, product_name: String) {
validate_product_name(&product_name);
self.product_name = product_name;
}
pub fn set_quantity(&mut self, quantity: u32) {
validate_quantity(&quantity);
self.quantity = quantity;
}
pub fn set_unit_price(&mut self, unit_price: u32) {
validate_unit_price(&unit_price);
self.unit_price = unit_price;
}
pub fn total(&self) -> u32 {
self.quantity * self.unit_price
}
}
fn validate_product_name(product_name: &String) {
if product_name.is_empty() {
panic!("Product name cannot be empty");
}
if product_name.len() > 300 {
panic!("Product name cannot be longer than 300 bytes");
}
}
fn validate_quantity(quantity: &u32) {
if quantity == &0 {
panic!("Quantity must be greater than zero");
}
}
fn validate_unit_price(unit_price: &u32) {
if unit_price == &0 {
panic!("Unit price must be greater than zero");
}
}

View File

@@ -1,6 +1,6 @@
fn intro() -> &'static str { fn intro() -> &'static str {
// TODO: fix me 👇 // TODO: fix me 👇
"I'm ready to learn about traits!" "I'm ready to __!"
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -3,22 +3,6 @@
// //
// Then implement the trait for `u32` and `i32`. // Then implement the trait for `u32` and `i32`.
pub trait IsEven {
fn is_even(self) -> bool;
}
impl IsEven for u32 {
fn is_even(self) -> bool {
self % 2 == 0
}
}
impl IsEven for i32 {
fn is_even(self) -> bool {
self % 2 == 0
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@@ -3,3 +3,9 @@
// a foreign type (`u32`, from `std`). // a foreign type (`u32`, from `std`).
// Look at the compiler error to get familiar with what it looks like. // Look at the compiler error to get familiar with what it looks like.
// Then delete the code below and move on to the next exercise. // Then delete the code below and move on to the next exercise.
impl PartialEq for u32 {
fn eq(&self, _other: &Self) -> bool {
todo!()
}
}

View File

@@ -8,13 +8,7 @@ struct Ticket {
// TODO: Implement the `PartialEq` trait for `Ticket`. // TODO: Implement the `PartialEq` trait for `Ticket`.
impl PartialEq for Ticket { impl PartialEq for Ticket {}
fn eq(&self, other: &Self) -> bool {
self.title == other.title
&& self.description == other.description
&& self.status == other.status
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@@ -8,7 +8,7 @@
// print both sides of the comparison to the terminal. // print both sides of the comparison to the terminal.
// If the compared type doesn't implement `Debug`, it doesn't know how to represent them! // If the compared type doesn't implement `Debug`, it doesn't know how to represent them!
#[derive(Debug, PartialEq)] #[derive(PartialEq)]
struct Ticket { struct Ticket {
title: String, title: String,
description: String, description: String,

View File

@@ -6,10 +6,7 @@
// collections (e.g. BTreeMap). // collections (e.g. BTreeMap).
/// Return the minimum of two values. /// Return the minimum of two values.
pub fn min<T>(left: T, right: T) -> T pub fn min<T>(left: T, right: T) -> T {
where
T: Ord,
{
if left <= right { if left <= right {
left left
} else { } else {

View File

@@ -6,11 +6,12 @@ fn test_saturating_u16() {
let b: SaturatingU16 = 5u8.into(); let b: SaturatingU16 = 5u8.into();
let c: SaturatingU16 = u16::MAX.into(); let c: SaturatingU16 = u16::MAX.into();
let d: SaturatingU16 = (&1u16).into(); let d: SaturatingU16 = (&1u16).into();
let e = &c;
assert_eq!(a + b, SaturatingU16::from(15u16)); assert_eq!(a + b, SaturatingU16::from(15u16));
assert_eq!(a + c, SaturatingU16::from(u16::MAX)); assert_eq!(a + c, SaturatingU16::from(u16::MAX));
assert_eq!(a + d, SaturatingU16::from(11u16)); assert_eq!(a + d, SaturatingU16::from(11u16));
assert_eq!(a + a, 20u16); assert_eq!(a + a, 20u16);
assert_eq!(a + 5u16, 15u16); assert_eq!(a + 5u16, 15u16);
assert_eq!(a + &u16::MAX, SaturatingU16::from(u16::MAX)); assert_eq!(a + e, SaturatingU16::from(u16::MAX));
} }

View File

@@ -43,7 +43,7 @@ mod tests {
} }
#[test] #[test]
fn thirthieth() { fn thirtieth() {
assert_eq!(fibonacci(30), 832040); assert_eq!(fibonacci(30), 832040);
} }
} }

View File

@@ -1,6 +1,6 @@
// TODO: Define a function named `lowercase` that converts all characters in a string to lowercase, // TODO: Define a function named `lowercase` that converts all characters in a string to lowercase,
// modifying the input in place. // modifying the input in place.
// Does it need to take a `&mut String`? Does a `&mut [str]` work? Why or why not? // Does it need to take a `&mut String`? Does a `&mut str` work? Why or why not?
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@@ -38,7 +38,7 @@ enum Command {
}, },
} }
pub fn server(receiver: Receiver<Command>) { fn server(receiver: Receiver<Command>) {
let mut store = TicketStore::new(); let mut store = TicketStore::new();
loop { loop {
match receiver.recv() { match receiver.recv() {