From 19006a8d0ca676d3baf0f304d01a5688a4adc547 Mon Sep 17 00:00:00 2001 From: yunusabdul Date: Fri, 28 Mar 2025 14:39:03 +0100 Subject: [PATCH] feat: trait exercise --- src/main.rs | 1 + src/traits/drop_trait.rs | 23 ++++++++ src/traits/eight_size_trait.rs | 7 +++ src/traits/eleven_clone_trait.rs | 28 +++++++++ src/traits/five_trait_bound.rs | 15 +++++ src/traits/four_derive_trait.rs | 90 ++++++++++++++++++++++++++++ src/traits/mod.rs | 14 +++++ src/traits/nine_from_trait.rs | 16 +++++ src/traits/one_trait.rs | 40 +++++++++++++ src/traits/outro_trait.rs | 10 ++++ src/traits/seven_deref_trait.rs | 38 ++++++++++++ src/traits/six_str_slice_trait.rs | 70 ++++++++++++++++++++++ src/traits/ten_assos_vs_generic.rs | 76 ++++++++++++++++++++++++ src/traits/three_operator_trait.rs | 94 ++++++++++++++++++++++++++++++ src/traits/twelve_copy_trait.rs | 35 +++++++++++ src/traits/two_trait.rs | 11 ++++ 16 files changed, 568 insertions(+) create mode 100644 src/traits/drop_trait.rs create mode 100644 src/traits/eight_size_trait.rs create mode 100644 src/traits/eleven_clone_trait.rs create mode 100644 src/traits/five_trait_bound.rs create mode 100644 src/traits/four_derive_trait.rs create mode 100644 src/traits/mod.rs create mode 100644 src/traits/nine_from_trait.rs create mode 100644 src/traits/one_trait.rs create mode 100644 src/traits/outro_trait.rs create mode 100644 src/traits/seven_deref_trait.rs create mode 100644 src/traits/six_str_slice_trait.rs create mode 100644 src/traits/ten_assos_vs_generic.rs create mode 100644 src/traits/three_operator_trait.rs create mode 100644 src/traits/twelve_copy_trait.rs create mode 100644 src/traits/two_trait.rs diff --git a/src/main.rs b/src/main.rs index 5ad08b7..f6cb2f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod float; mod signed; mod string; mod todo; +mod traits; mod unsigned; mod user_struct; diff --git a/src/traits/drop_trait.rs b/src/traits/drop_trait.rs new file mode 100644 index 0000000..b1f2751 --- /dev/null +++ b/src/traits/drop_trait.rs @@ -0,0 +1,23 @@ +// // TODO: implement a so-called "Drop bomb": a type that panics when dropped +// // unless a certain operation has been performed on it. +// // You can see the expected API in the tests below. + +// #[cfg(test)] +// mod tests { +// use super::*; + +// #[test] +// #[should_panic] +// fn test_drop_bomb() { +// let bomb = DropBomb::new(); +// // The bomb should panic when dropped +// } + +// #[test] +// fn test_defused_drop_bomb() { +// let mut bomb = DropBomb::new(); +// bomb.defuse(); +// // The bomb should not panic when dropped +// // since it has been defused +// } +// } diff --git a/src/traits/eight_size_trait.rs b/src/traits/eight_size_trait.rs new file mode 100644 index 0000000..0c8e7ee --- /dev/null +++ b/src/traits/eight_size_trait.rs @@ -0,0 +1,7 @@ +pub fn example() { + // Trying to get the size of a str (or any other DST) + // via `std::mem::size_of` will result in a compile-time error. + // + // TODO: Comment out the following line and move on to the next exercise. + std::mem::size_of::<&str>(); +} diff --git a/src/traits/eleven_clone_trait.rs b/src/traits/eleven_clone_trait.rs new file mode 100644 index 0000000..d74045d --- /dev/null +++ b/src/traits/eleven_clone_trait.rs @@ -0,0 +1,28 @@ +// TODO: add the necessary `Clone` implementations (and invocations) +// to get the code to compile. + +pub fn summary(ticket: Ticket) -> (Ticket, Summary) { + let m = ticket.clone().summary(); + (ticket, m) +} + +#[derive(Clone)] +pub struct Ticket { + pub title: String, + pub description: String, + pub status: String, +} + +impl Ticket { + pub fn summary(self) -> Summary { + Summary { + title: self.title, + status: self.status, + } + } +} + +pub struct Summary { + pub title: String, + pub status: String, +} diff --git a/src/traits/five_trait_bound.rs b/src/traits/five_trait_bound.rs new file mode 100644 index 0000000..59e53a2 --- /dev/null +++ b/src/traits/five_trait_bound.rs @@ -0,0 +1,15 @@ +// TODO: Add the necessary trait bounds to `min` so that it compiles successfully. +// Refer to the documentation of the `std::cmp` module for more information on the traits you might need. +// +// Note: there are different trait bounds that'll make the compiler happy, but they come with +// different _semantics_. We'll cover those differences later in the course when we talk about ordered +// collections (e.g. BTreeMap). + +/// Return the minimum of two values. +pub fn min(left: T, right: T) -> T { + if left <= right { + left + } else { + right + } +} diff --git a/src/traits/four_derive_trait.rs b/src/traits/four_derive_trait.rs new file mode 100644 index 0000000..d39d6c5 --- /dev/null +++ b/src/traits/four_derive_trait.rs @@ -0,0 +1,90 @@ +// TODO: A (derivable) trait implementation is missing for this exercise to compile successfully. +// Fix it! +// +// # `Debug` primer +// +// `Debug` returns a representation of a Rust type that's suitable for debugging (hence the name). +// `assert_eq!` requires `Ticket` to implement `Debug` because, when the assertion fails, it tries to +// 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! + +#[derive(PartialEq, Debug)] +struct Ticket { + title: String, + description: String, + status: String, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_partial_eq() { + let title = "title"; + let description = "description"; + let status = "To-Do"; + let ticket1 = Ticket { + title: title.to_string(), + description: description.to_string(), + status: status.to_string(), + }; + let ticket2 = Ticket { + title: title.to_string(), + description: description.to_string(), + status: status.to_string(), + }; + assert_eq!(ticket1, ticket2); + } + + #[test] + fn test_description_not_matching() { + let title = "title"; + let status = "To-Do"; + let ticket1 = Ticket { + title: title.to_string(), + description: "description".to_string(), + status: status.to_string(), + }; + let ticket2 = Ticket { + title: title.to_string(), + description: "description2".to_string(), + status: status.to_string(), + }; + assert_ne!(ticket1, ticket2); + } + + #[test] + fn test_title_not_matching() { + let status = "To-Do"; + let description = "description"; + let ticket1 = Ticket { + title: "title".to_string(), + description: description.to_string(), + status: status.to_string(), + }; + let ticket2 = Ticket { + title: "title2".to_string(), + description: description.to_string(), + status: status.to_string(), + }; + assert_ne!(ticket1, ticket2); + } + + #[test] + fn test_status_not_matching() { + let title = "title"; + let description = "description"; + let ticket1 = Ticket { + title: title.to_string(), + description: description.to_string(), + status: "status".to_string(), + }; + let ticket2 = Ticket { + title: title.to_string(), + description: description.to_string(), + status: "status2".to_string(), + }; + assert_ne!(ticket1, ticket2); + } +} diff --git a/src/traits/mod.rs b/src/traits/mod.rs new file mode 100644 index 0000000..3cd7777 --- /dev/null +++ b/src/traits/mod.rs @@ -0,0 +1,14 @@ +pub mod drop_trait; +pub mod eight_size_trait; +pub mod eleven_clone_trait; +pub mod five_trait_bound; +pub mod four_derive_trait; +pub mod nine_from_trait; +pub mod one_trait; +pub mod outro_trait; +pub mod seven_deref_trait; +pub mod six_str_slice_trait; +pub mod ten_assos_vs_generic; +pub mod three_operator_trait; +pub mod twelve_copy_trait; +pub mod two_trait; diff --git a/src/traits/nine_from_trait.rs b/src/traits/nine_from_trait.rs new file mode 100644 index 0000000..b528bea --- /dev/null +++ b/src/traits/nine_from_trait.rs @@ -0,0 +1,16 @@ +// TODO: Implement the `From` trait for the `WrappingU32` type to make `example` compile. + +pub struct WrappingU32 { + value: u32, +} + +impl From for WrappingU32 { + fn from(value: u32) -> Self { + Self { value } + } +} + +fn example() { + let wrapping: WrappingU32 = 42.into(); + let wrapping = WrappingU32::from(42); +} diff --git a/src/traits/one_trait.rs b/src/traits/one_trait.rs new file mode 100644 index 0000000..ebfefa3 --- /dev/null +++ b/src/traits/one_trait.rs @@ -0,0 +1,40 @@ +use std::ops::Rem; + +// Define a trait named `IsEven` that has a method `is_even` that returns a `true` if `self` is +// even, otherwise `false`. +// +// 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)] +mod tests { + use super::*; + + #[test] + fn test_u32_is_even() { + assert!(42u32.is_even()); + assert!(!43u32.is_even()); + } + + #[test] + fn test_i32_is_even() { + assert!(42i32.is_even()); + assert!(!43i32.is_even()); + assert!(0i32.is_even()); + assert!(!(-1i32).is_even()); + } +} diff --git a/src/traits/outro_trait.rs b/src/traits/outro_trait.rs new file mode 100644 index 0000000..547fd94 --- /dev/null +++ b/src/traits/outro_trait.rs @@ -0,0 +1,10 @@ +// TODO: Define a new `SaturatingU16` type. +// It should hold a `u16` value. +// It should provide conversions from `u16`, `u8`, `&u16` and `&u8`. +// It should support addition with a right-hand side of type +// SaturatingU16, u16, &u16, and &SaturatingU16. Addition should saturate at the +// maximum value for `u16`. +// It should be possible to compare it with another `SaturatingU16` or a `u16`. +// It should be possible to print its debug representation. +// +// Tests are located in the `tests` folder—pay attention to the visibility of your types and methods. diff --git a/src/traits/seven_deref_trait.rs b/src/traits/seven_deref_trait.rs new file mode 100644 index 0000000..6eb6589 --- /dev/null +++ b/src/traits/seven_deref_trait.rs @@ -0,0 +1,38 @@ +// TODO: whenever `title` and `description` are returned via their accessor methods, they +// should be normalized—i.e. leading and trailing whitespace should be removed. +// There is a method in Rust's standard library that can help with this, but you won't +// find it in the documentation for `String`. +// Can you figure out where it is defined and how to use it? + +pub struct Ticket { + title: String, + description: String, + status: String, +} + +impl Ticket { + pub fn title(&self) -> &str { + self.title.as_str().trim() + } + + pub fn description(&self) -> &str { + self.description.as_str().trim() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_normalization() { + let ticket = Ticket { + title: " A title ".to_string(), + description: " A description ".to_string(), + status: "To-Do".to_string(), + }; + + assert_eq!("A title", ticket.title()); + assert_eq!("A description", ticket.description()); + } +} diff --git a/src/traits/six_str_slice_trait.rs b/src/traits/six_str_slice_trait.rs new file mode 100644 index 0000000..5df52bb --- /dev/null +++ b/src/traits/six_str_slice_trait.rs @@ -0,0 +1,70 @@ +// TODO: Re-implement `Ticket`'s accessor methods. This time return a `&str` rather than a `&String`. + +pub struct Ticket { + title: String, + description: String, + status: String, +} + +impl Ticket { + pub fn new(title: String, description: String, status: String) -> Ticket { + if title.is_empty() { + panic!("Title cannot be empty"); + } + 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 { + title, + description, + status, + } + } + + pub fn title(&self) -> &str { + &self.title.as_str() + } + + pub fn description(&self) -> &str { + &self.description.as_str() + } + + pub fn status(&self) -> &str { + &self.status.as_str() + } +} + +mod common { + pub fn valid_description() -> String { + "fan of a fan".to_string() + } + pub fn valid_title() -> String { + "INDIGO".to_string() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use common::{valid_description, valid_title}; + use std::any::{Any, TypeId}; + + #[test] + fn test_type() { + let ticket = Ticket::new(valid_title(), valid_description(), "To-Do".to_string()); + // Some dark magic to verify that you used the expected return types + assert_eq!(TypeId::of::(), ticket.title().type_id()); + assert_eq!(TypeId::of::(), ticket.description().type_id()); + assert_eq!(TypeId::of::(), ticket.status().type_id()); + } +} diff --git a/src/traits/ten_assos_vs_generic.rs b/src/traits/ten_assos_vs_generic.rs new file mode 100644 index 0000000..ec788d9 --- /dev/null +++ b/src/traits/ten_assos_vs_generic.rs @@ -0,0 +1,76 @@ +// TODO: Define a new trait, `Power`, that has a method `power` that raises `self` +// to the power of `n`. +// The trait definition and its implementations should be enough to get +// the tests to compile and pass. +// +// Recommendation: you may be tempted to write a generic implementation to handle +// all cases at once. However, this is fairly complicated and requires the use of +// additional crates (i.e. `num-traits`). +// Even then, it might be preferable to use a simple macro instead to avoid +// the complexity of a highly generic implementation. Check out the +// "Little book of Rust macros" (https://veykril.github.io/tlborm/) if you're +// interested in learning more about it. +// You don't have to though: it's perfectly okay to write three separate +// implementations manually. Venture further only if you're curious. + +// generic type implementation but does +// not include refrence type + +// pub trait Power { +// fn power(&self,other:T)-> u32; +// } + +// impl> Power for u32 { +// fn power(&self,other:T)-> u32 { +// let right = other.into(); +// self.pow(right) +// } +// } + +pub trait Power { + fn power(&self, other: Rhs) -> u32; +} + +impl Power for u32 { + fn power(&self, other: u16) -> u32 { + let right = other as u32; + self.pow(right) + } +} + +impl Power for u32 { + fn power(&self, other: u32) -> u32 { + let right = other as u32; + self.pow(right) + } +} + +impl Power<&u32> for u32 { + fn power(&self, other: &u32) -> u32 { + let right = *other as u32; + self.pow(right) + } +} + +#[cfg(test)] +mod tests { + use super::Power; + + #[test] + fn test_power_u16() { + let x: u32 = 2_u32.power(3u16); + assert_eq!(x, 8); + } + + #[test] + fn test_power_u32() { + let x: u32 = 2_u32.power(3u32); + assert_eq!(x, 8); + } + + #[test] + fn test_power_ref_u32() { + let x: u32 = 2_u32.power(&3u32); + assert_eq!(x, 8); + } +} diff --git a/src/traits/three_operator_trait.rs b/src/traits/three_operator_trait.rs new file mode 100644 index 0000000..fa55ec3 --- /dev/null +++ b/src/traits/three_operator_trait.rs @@ -0,0 +1,94 @@ +use std::cmp::PartialEq; + +struct Ticket { + title: String, + description: String, + status: String, +} + +// TODO: Implement the `PartialEq` trait for `Ticket`. + +impl PartialEq for Ticket { + fn eq(&self, other: &Self) -> bool { + self.description == other.description + && self.status == other.status + && self.title == other.title + } + fn ne(&self, other: &Self) -> bool { + !self.eq(other) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_partial_eq() { + let title = "title"; + let description = "description"; + let status = "To-Do"; + let ticket1 = Ticket { + title: title.to_string(), + description: description.to_string(), + status: status.to_string(), + }; + let ticket2 = Ticket { + title: title.to_string(), + description: description.to_string(), + status: status.to_string(), + }; + assert!(ticket1 == ticket2); + } + + #[test] + fn test_description_not_matching() { + let title = "title"; + let status = "To-Do"; + let ticket1 = Ticket { + title: title.to_string(), + description: "description".to_string(), + status: status.to_string(), + }; + let ticket2 = Ticket { + title: title.to_string(), + description: "description2".to_string(), + status: status.to_string(), + }; + assert!(ticket1 != ticket2); + } + + #[test] + fn test_title_not_matching() { + let status = "To-Do"; + let description = "description"; + let ticket1 = Ticket { + title: "title".to_string(), + description: description.to_string(), + status: status.to_string(), + }; + let ticket2 = Ticket { + title: "title2".to_string(), + description: description.to_string(), + status: status.to_string(), + }; + assert!(ticket1 != ticket2); + } + + #[test] + fn test_status_not_matching() { + let title = "title"; + let description = "description"; + let ticket1 = Ticket { + title: title.to_string(), + description: description.to_string(), + status: "status".to_string(), + }; + let ticket2 = Ticket { + title: title.to_string(), + description: description.to_string(), + status: "status2".to_string(), + }; + assert!(ticket1 != ticket2); + } +} diff --git a/src/traits/twelve_copy_trait.rs b/src/traits/twelve_copy_trait.rs new file mode 100644 index 0000000..3c9d3a5 --- /dev/null +++ b/src/traits/twelve_copy_trait.rs @@ -0,0 +1,35 @@ +// TODO: implement the necessary traits to make the test compile and pass. +// You *can't* modify the test. + +use std::ops::Add; + +#[derive(PartialEq, Debug, Clone, Copy)] +pub struct WrappingU32 { + value: u32, +} + +impl WrappingU32 { + pub fn new(value: u32) -> Self { + Self { value } + } +} + +impl Add for WrappingU32 { + type Output = WrappingU32; + fn add(self, rhs: Self) -> Self::Output { + self + rhs + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ops() { + let x = WrappingU32::new(42); + let y = WrappingU32::new(31); + let z = WrappingU32::new(u32::MAX); + assert_eq!(x + y + y + z, WrappingU32::new(103)); + } +} diff --git a/src/traits/two_trait.rs b/src/traits/two_trait.rs new file mode 100644 index 0000000..e862fa8 --- /dev/null +++ b/src/traits/two_trait.rs @@ -0,0 +1,11 @@ +// TODO: this is an example of an orphan rule violation. +// We're implementing a foreign trait (`PartialEq`, from `std`) on +// a foreign type (`u32`, from `std`). +// 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. + +// impl PartialEq for u32 { +// fn eq(&self, _other: &Self) -> bool { +// todo!() +// } +// }