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!
generics1
lifetimes1
intro1
intro2
@ -58,3 +58,11 @@ errors3
errors4
errors5
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
// `Vec<T>`. Choose `T` as some integer type that can be created from
// `u8` and `i8`.
let mut numbers = Vec::new();
let mut numbers: Vec<i32> = Vec::new();
// Don't change the lines below.
let n1: u8 = 42;

View file

@ -1,12 +1,12 @@
// 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.
struct Wrapper {
value: u32,
struct Wrapper<T> {
value: T,
}
// TODO: Adapt the struct's implementation to be generic over the wrapped value.
impl Wrapper {
fn new(value: u32) -> Self {
impl <T> Wrapper<T> {
fn new(value: T) -> Self {
Wrapper { value }
}
}

View file

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

View file

@ -4,6 +4,12 @@ trait AppendBar {
// TODO: Implement the trait `AppendBar` for a vector of strings.
// `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() {
// You can optionally experiment here.

View file

@ -5,7 +5,9 @@ trait Licensed {
// implementors like the two structs below can share that default behavior
// without repeating the function.
// 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 {

View file

@ -11,7 +11,8 @@ impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}
// 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()
}

View file

@ -19,7 +19,8 @@ impl SomeTrait for OtherStruct {}
impl OtherTrait for OtherStruct {}
// 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()
}

View file

@ -11,15 +11,17 @@
// 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;
// TODO: Adjust the struct as described above.
struct ReportCard {
grade: f32,
struct ReportCard<T> where T: Display {
grade: T,
student_name: String,
student_age: u8,
}
// TODO: Adjust the impl block as described above.
impl ReportCard {
impl <T: Display>ReportCard<T> {
fn print(&self) -> String {
format!(
"{} ({}) - 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() {
// DON'T EDIT THIS SOLUTION FILE!
// It will be automatically filled after you finish the exercise.
// `u8` and `i8` can both be converted to `i16`.
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() {
// DON'T EDIT THIS SOLUTION FILE!
// It will be automatically filled after you finish the exercise.
struct Wrapper<T> {
value: T,
}
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() {
// DON'T EDIT THIS SOLUTION FILE!
// It will be automatically filled after you finish the exercise.
// The trait `AppendBar` has only one function which appends "Bar" to any object
// implementing this trait.
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() {
// DON'T EDIT THIS SOLUTION FILE!
// It will be automatically filled after you finish the exercise.
trait AppendBar {
fn append_bar(self) -> Self;
}
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() {
// DON'T EDIT THIS SOLUTION FILE!
// It will be automatically filled after you finish the exercise.
#![allow(dead_code)]
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() {
// 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;
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() {
// DON'T EDIT THIS SOLUTION FILE!
// It will be automatically filled after you finish the exercise.
trait SomeTrait {
fn some_function(&self) -> bool {
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() {
// DON'T EDIT THIS SOLUTION FILE!
// It will be automatically filled after you finish the exercise.
// An imaginary magical school has a new report card generation system written
// in Rust! Currently, the system only supports creating report cards where the
// 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+",
);
}
}