progress through generics and traits

This commit is contained in:
Denis-Cosmin Nutiu 2024-11-02 16:24:22 +02:00
parent 7e6588f6f0
commit 3fcd3d8e03
17 changed files with 309 additions and 37 deletions

View file

@ -1,6 +1,6 @@
DON'T EDIT THIS FILE! DON'T EDIT THIS FILE!
generics1 lifetimes1
intro1 intro1
intro2 intro2
@ -57,4 +57,12 @@ errors2
errors3 errors3
errors4 errors4
errors5 errors5
errors6 errors6
generics1
generics2
traits1
traits2
traits3
traits4
traits5
quiz3

View file

@ -6,7 +6,7 @@ fn main() {
// TODO: Fix the compiler error by annotating the type of the vector // TODO: Fix the compiler error by annotating the type of the vector
// `Vec<T>`. Choose `T` as some integer type that can be created from // `Vec<T>`. Choose `T` as some integer type that can be created from
// `u8` and `i8`. // `u8` and `i8`.
let mut numbers = Vec::new(); let mut numbers: Vec<i32> = Vec::new();
// Don't change the lines below. // Don't change the lines below.
let n1: u8 = 42; let n1: u8 = 42;

View file

@ -1,12 +1,12 @@
// This powerful wrapper provides the ability to store a positive integer value. // This powerful wrapper provides the ability to store a positive integer value.
// TODO: Rewrite it using a generic so that it supports wrapping ANY type. // TODO: Rewrite it using a generic so that it supports wrapping ANY type.
struct Wrapper { struct Wrapper<T> {
value: u32, value: T,
} }
// TODO: Adapt the struct's implementation to be generic over the wrapped value. // TODO: Adapt the struct's implementation to be generic over the wrapped value.
impl Wrapper { impl <T> Wrapper<T> {
fn new(value: u32) -> Self { fn new(value: T) -> Self {
Wrapper { value } Wrapper { value }
} }
} }

View file

@ -5,7 +5,10 @@ trait AppendBar {
} }
impl AppendBar for String { impl AppendBar for String {
// TODO: Implement `AppendBar` for the type `String`. fn append_bar(mut self) -> Self {
self.push_str("Bar");
self
}
} }
fn main() { fn main() {

View file

@ -4,6 +4,12 @@ trait AppendBar {
// TODO: Implement the trait `AppendBar` for a vector of strings. // TODO: Implement the trait `AppendBar` for a vector of strings.
// `append_bar` should push the string "Bar" into the vector. // `append_bar` should push the string "Bar" into the vector.
impl AppendBar for Vec<String> {
fn append_bar(mut self) -> Self {
self.push(String::from("Bar"));
self
}
}
fn main() { fn main() {
// You can optionally experiment here. // You can optionally experiment here.

View file

@ -5,7 +5,9 @@ trait Licensed {
// implementors like the two structs below can share that default behavior // implementors like the two structs below can share that default behavior
// without repeating the function. // without repeating the function.
// The default license information should be the string "Default license". // The default license information should be the string "Default license".
fn licensing_info(&self) -> String; fn licensing_info(&self) -> String {
return String::from("Default license")
}
} }
struct SomeSoftware { struct SomeSoftware {

View file

@ -11,7 +11,8 @@ impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {} impl Licensed for OtherSoftware {}
// TODO: Fix the compiler error by only changing the signature of this function. // TODO: Fix the compiler error by only changing the signature of this function.
fn compare_license_types(software1: ???, software2: ???) -> bool { fn compare_license_types<T, F>(software1: T, software2: F) -> bool
where T: Licensed, F: Licensed{
software1.licensing_info() == software2.licensing_info() software1.licensing_info() == software2.licensing_info()
} }

View file

@ -19,7 +19,8 @@ impl SomeTrait for OtherStruct {}
impl OtherTrait for OtherStruct {} impl OtherTrait for OtherStruct {}
// TODO: Fix the compiler error by only changing the signature of this function. // TODO: Fix the compiler error by only changing the signature of this function.
fn some_func(item: ???) -> bool { fn some_func<T>(item: T) -> bool
where T: OtherTrait + SomeTrait{
item.some_function() && item.other_function() item.some_function() && item.other_function()
} }

View file

@ -11,15 +11,17 @@
// Make the necessary code changes in the struct `ReportCard` and the impl // Make the necessary code changes in the struct `ReportCard` and the impl
// block to support alphabetical report cards in addition to numerical ones. // block to support alphabetical report cards in addition to numerical ones.
use std::fmt::Display;
// TODO: Adjust the struct as described above. // TODO: Adjust the struct as described above.
struct ReportCard { struct ReportCard<T> where T: Display {
grade: f32, grade: T,
student_name: String, student_name: String,
student_age: u8, student_age: u8,
} }
// TODO: Adjust the impl block as described above. // TODO: Adjust the impl block as described above.
impl ReportCard { impl <T: Display>ReportCard<T> {
fn print(&self) -> String { fn print(&self) -> String {
format!( format!(
"{} ({}) - achieved a grade of {}", "{} ({}) - achieved a grade of {}",

View file

@ -1,4 +1,17 @@
// `Vec<T>` is generic over the type `T`. In most cases, the compiler is able to
// infer `T`, for example after pushing a value with a concrete type to the vector.
// But in this exercise, the compiler needs some help through a type annotation.
fn main() { fn main() {
// DON'T EDIT THIS SOLUTION FILE! // `u8` and `i8` can both be converted to `i16`.
// It will be automatically filled after you finish the exercise. let mut numbers: Vec<i16> = Vec::new();
// ^^^^^^^^^^ added
// Don't change the lines below.
let n1: u8 = 42;
numbers.push(n1.into());
let n2: i8 = -1;
numbers.push(n2.into());
println!("{numbers:?}");
} }

View file

@ -1,4 +1,28 @@
fn main() { struct Wrapper<T> {
// DON'T EDIT THIS SOLUTION FILE! value: T,
// It will be automatically filled after you finish the exercise. }
impl<T> Wrapper<T> {
fn new(value: T) -> Self {
Wrapper { value }
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn store_u32_in_wrapper() {
assert_eq!(Wrapper::new(42).value, 42);
}
#[test]
fn store_str_in_wrapper() {
assert_eq!(Wrapper::new("Foo").value, "Foo");
}
} }

View file

@ -1,4 +1,32 @@
fn main() { // The trait `AppendBar` has only one function which appends "Bar" to any object
// DON'T EDIT THIS SOLUTION FILE! // implementing this trait.
// It will be automatically filled after you finish the exercise. trait AppendBar {
fn append_bar(self) -> Self;
}
impl AppendBar for String {
fn append_bar(self) -> Self {
self + "Bar"
}
}
fn main() {
let s = String::from("Foo");
let s = s.append_bar();
println!("s: {s}");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_foo_bar() {
assert_eq!(String::from("Foo").append_bar(), "FooBar");
}
#[test]
fn is_bar_bar() {
assert_eq!(String::from("").append_bar().append_bar(), "BarBar");
}
} }

View file

@ -1,4 +1,27 @@
fn main() { trait AppendBar {
// DON'T EDIT THIS SOLUTION FILE! fn append_bar(self) -> Self;
// It will be automatically filled after you finish the exercise. }
impl AppendBar for Vec<String> {
fn append_bar(mut self) -> Self {
// ^^^ this is important
self.push(String::from("Bar"));
self
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_vec_pop_eq_bar() {
let mut foo = vec![String::from("Foo")].append_bar();
assert_eq!(foo.pop().unwrap(), "Bar");
assert_eq!(foo.pop().unwrap(), "Foo");
}
} }

View file

@ -1,4 +1,38 @@
fn main() { #![allow(dead_code)]
// DON'T EDIT THIS SOLUTION FILE!
// It will be automatically filled after you finish the exercise. trait Licensed {
fn licensing_info(&self) -> String {
"Default license".to_string()
}
}
struct SomeSoftware {
version_number: i32,
}
struct OtherSoftware {
version_number: String,
}
impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_licensing_info_the_same() {
let licensing_info = "Default license";
let some_software = SomeSoftware { version_number: 1 };
let other_software = OtherSoftware {
version_number: "v2.0.0".to_string(),
};
assert_eq!(some_software.licensing_info(), licensing_info);
assert_eq!(other_software.licensing_info(), licensing_info);
}
} }

View file

@ -1,4 +1,35 @@
fn main() { trait Licensed {
// DON'T EDIT THIS SOLUTION FILE! fn licensing_info(&self) -> String {
// It will be automatically filled after you finish the exercise. "Default license".to_string()
}
}
struct SomeSoftware;
struct OtherSoftware;
impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}
fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool {
// ^^^^^^^^^^^^^ ^^^^^^^^^^^^^
software1.licensing_info() == software2.licensing_info()
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compare_license_information() {
assert!(compare_license_types(SomeSoftware, OtherSoftware));
}
#[test]
fn compare_license_information_backwards() {
assert!(compare_license_types(OtherSoftware, SomeSoftware));
}
} }

View file

@ -1,4 +1,39 @@
fn main() { trait SomeTrait {
// DON'T EDIT THIS SOLUTION FILE! fn some_function(&self) -> bool {
// It will be automatically filled after you finish the exercise. true
}
}
trait OtherTrait {
fn other_function(&self) -> bool {
true
}
}
struct SomeStruct;
impl SomeTrait for SomeStruct {}
impl OtherTrait for SomeStruct {}
struct OtherStruct;
impl SomeTrait for OtherStruct {}
impl OtherTrait for OtherStruct {}
fn some_func(item: impl SomeTrait + OtherTrait) -> bool {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
item.some_function() && item.other_function()
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_some_func() {
assert!(some_func(SomeStruct));
assert!(some_func(OtherStruct));
}
} }

View file

@ -1,4 +1,65 @@
fn main() { // An imaginary magical school has a new report card generation system written
// DON'T EDIT THIS SOLUTION FILE! // in Rust! Currently, the system only supports creating report cards where the
// It will be automatically filled after you finish the exercise. // student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the
// school also issues alphabetical grades (A+ -> F-) and needs to be able to
// print both types of report card!
//
// Make the necessary code changes in the struct `ReportCard` and the impl
// block to support alphabetical report cards in addition to numerical ones.
use std::fmt::Display;
// Make the struct generic over `T`.
struct ReportCard<T> {
// ^^^
grade: T,
// ^
student_name: String,
student_age: u8,
}
// To be able to print the grade, it has to implement the `Display` trait.
impl<T: Display> ReportCard<T> {
// ^^^^^^^ require that `T` implements `Display`.
fn print(&self) -> String {
format!(
"{} ({}) - achieved a grade of {}",
&self.student_name, &self.student_age, &self.grade,
)
}
}
fn main() {
// You can optionally experiment here.
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generate_numeric_report_card() {
let report_card = ReportCard {
grade: 2.1,
student_name: "Tom Wriggle".to_string(),
student_age: 12,
};
assert_eq!(
report_card.print(),
"Tom Wriggle (12) - achieved a grade of 2.1",
);
}
#[test]
fn generate_alphabetic_report_card() {
let report_card = ReportCard {
grade: "A+",
student_name: "Gary Plotter".to_string(),
student_age: 11,
};
assert_eq!(
report_card.print(),
"Gary Plotter (11) - achieved a grade of A+",
);
}
} }