mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-01-11 00:00:04 +03:00
Compare commits
38 commits
129884aff7
...
746cf6863d
Author | SHA1 | Date | |
---|---|---|---|
746cf6863d | |||
803e32dad2 | |||
a4f8826301 | |||
6187216606 | |||
275a854d6e | |||
7efccc36b4 | |||
64c2de95ca | |||
c170740423 | |||
45cfe86fb0 | |||
db4d649e55 | |||
c0452d160b | |||
b4b7ae63ad | |||
c07209b635 | |||
091e1e7f7a | |||
92f249a52c | |||
e6228e92b4 | |||
3e9c4c8bb8 | |||
22b650c092 | |||
f0849447ad | |||
789223cc9e | |||
de3f846a53 | |||
46121b71cf | |||
b1daea1fe8 | |||
42b5c0a1f7 | |||
1ede3a82e9 | |||
d140d49b61 | |||
5337620476 | |||
c7a18b0781 | |||
01a78531ad | |||
c2414b8891 | |||
f03020a7e2 | |||
01509a2a84 | |||
66b3a9cdd7 | |||
baca8c9667 | |||
8c3b8dcec4 | |||
881d3e9441 | |||
4eec81a113 | |||
9a13bccd63 |
9
.github/workflows/rust.yml
vendored
9
.github/workflows/rust.yml
vendored
|
@ -2,14 +2,19 @@ name: Rustlings Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
clippy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: cargo clippy -- --deny warnings
|
||||||
fmt:
|
fmt:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
|
@ -60,8 +60,6 @@ bin = [
|
||||||
{ name = "move_semantics4_sol", path = "../solutions/06_move_semantics/move_semantics4.rs" },
|
{ name = "move_semantics4_sol", path = "../solutions/06_move_semantics/move_semantics4.rs" },
|
||||||
{ name = "move_semantics5", path = "../exercises/06_move_semantics/move_semantics5.rs" },
|
{ name = "move_semantics5", path = "../exercises/06_move_semantics/move_semantics5.rs" },
|
||||||
{ name = "move_semantics5_sol", path = "../solutions/06_move_semantics/move_semantics5.rs" },
|
{ name = "move_semantics5_sol", path = "../solutions/06_move_semantics/move_semantics5.rs" },
|
||||||
{ name = "move_semantics6", path = "../exercises/06_move_semantics/move_semantics6.rs" },
|
|
||||||
{ name = "move_semantics6_sol", path = "../solutions/06_move_semantics/move_semantics6.rs" },
|
|
||||||
{ name = "structs1", path = "../exercises/07_structs/structs1.rs" },
|
{ name = "structs1", path = "../exercises/07_structs/structs1.rs" },
|
||||||
{ name = "structs1_sol", path = "../solutions/07_structs/structs1.rs" },
|
{ name = "structs1_sol", path = "../solutions/07_structs/structs1.rs" },
|
||||||
{ name = "structs2", path = "../exercises/07_structs/structs2.rs" },
|
{ name = "structs2", path = "../exercises/07_structs/structs2.rs" },
|
||||||
|
@ -142,8 +140,6 @@ bin = [
|
||||||
{ name = "tests2_sol", path = "../solutions/17_tests/tests2.rs" },
|
{ name = "tests2_sol", path = "../solutions/17_tests/tests2.rs" },
|
||||||
{ name = "tests3", path = "../exercises/17_tests/tests3.rs" },
|
{ name = "tests3", path = "../exercises/17_tests/tests3.rs" },
|
||||||
{ name = "tests3_sol", path = "../solutions/17_tests/tests3.rs" },
|
{ name = "tests3_sol", path = "../solutions/17_tests/tests3.rs" },
|
||||||
{ name = "tests4", path = "../exercises/17_tests/tests4.rs" },
|
|
||||||
{ name = "tests4_sol", path = "../solutions/17_tests/tests4.rs" },
|
|
||||||
{ name = "iterators1", path = "../exercises/18_iterators/iterators1.rs" },
|
{ name = "iterators1", path = "../exercises/18_iterators/iterators1.rs" },
|
||||||
{ name = "iterators1_sol", path = "../solutions/18_iterators/iterators1.rs" },
|
{ name = "iterators1_sol", path = "../solutions/18_iterators/iterators1.rs" },
|
||||||
{ name = "iterators2", path = "../exercises/18_iterators/iterators2.rs" },
|
{ name = "iterators2", path = "../exercises/18_iterators/iterators2.rs" },
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// A basket of fruits in the form of a hash map needs to be defined. The key
|
// A basket of fruits in the form of a hash map needs to be defined. The key
|
||||||
// represents the name of the fruit and the value represents how many of that
|
// represents the name of the fruit and the value represents how many of that
|
||||||
// particular fruit is in the basket. You have to put at least 3 different
|
// particular fruit is in the basket. You have to put at least 3 different
|
||||||
// types of fruits (e.g apple, banana, mango) in the basket and the total count
|
// types of fruits (e.g. apple, banana, mango) in the basket and the total count
|
||||||
// of all the fruits should be at least 5.
|
// of all the fruits should be at least 5.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
|
@ -1,52 +1,51 @@
|
||||||
// Using catch-all error types like `Box<dyn error::Error>` isn't recommended
|
// Using catch-all error types like `Box<dyn Error>` isn't recommended for
|
||||||
// for library code, where callers might want to make decisions based on the
|
// library code where callers might want to make decisions based on the error
|
||||||
// error content, instead of printing it out or propagating it further. Here, we
|
// content instead of printing it out or propagating it further. Here, we define
|
||||||
// define a custom error type to make it possible for callers to decide what to
|
// a custom error type to make it possible for callers to decide what to do next
|
||||||
// do next when our function returns an error.
|
// when our function returns an error.
|
||||||
|
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
// This is a custom error type that we will be using in `parse_pos_nonzero()`.
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
enum ParsePosNonzeroError {
|
|
||||||
Creation(CreationError),
|
|
||||||
ParseInt(ParseIntError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParsePosNonzeroError {
|
|
||||||
fn from_creation(err: CreationError) -> ParsePosNonzeroError {
|
|
||||||
ParsePosNonzeroError::Creation(err)
|
|
||||||
}
|
|
||||||
// TODO: add another error conversion function here.
|
|
||||||
// fn from_parseint...
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_pos_nonzero(s: &str) -> Result<PositiveNonzeroInteger, ParsePosNonzeroError> {
|
|
||||||
// TODO: change this to return an appropriate error instead of panicking
|
|
||||||
// when `parse()` returns an error.
|
|
||||||
let x: i64 = s.parse().unwrap();
|
|
||||||
PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't change anything below this line.
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
struct PositiveNonzeroInteger(u64);
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
enum CreationError {
|
enum CreationError {
|
||||||
Negative,
|
Negative,
|
||||||
Zero,
|
Zero,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum ParsePosNonzeroError {
|
||||||
|
Creation(CreationError),
|
||||||
|
ParseInt(ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParsePosNonzeroError {
|
||||||
|
fn from_creation(err: CreationError) -> Self {
|
||||||
|
Self::Creation(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add another error conversion function here.
|
||||||
|
// fn from_parseint(???) -> Self { ??? }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
impl PositiveNonzeroInteger {
|
impl PositiveNonzeroInteger {
|
||||||
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
fn new(value: i64) -> Result<Self, CreationError> {
|
||||||
match value {
|
match value {
|
||||||
x if x < 0 => Err(CreationError::Negative),
|
x if x < 0 => Err(CreationError::Negative),
|
||||||
x if x == 0 => Err(CreationError::Zero),
|
x if x == 0 => Err(CreationError::Zero),
|
||||||
x => Ok(PositiveNonzeroInteger(x as u64)),
|
x => Ok(Self(x as u64)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
|
||||||
|
// TODO: change this to return an appropriate error instead of panicking
|
||||||
|
// when `parse()` returns an error.
|
||||||
|
let x: i64 = s.parse().unwrap();
|
||||||
|
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -56,36 +55,36 @@ fn main() {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::num::IntErrorKind;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_error() {
|
fn test_parse_error() {
|
||||||
// We can't construct a ParseIntError, so we have to pattern match.
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
parse_pos_nonzero("not a number"),
|
PositiveNonzeroInteger::parse("not a number"),
|
||||||
Err(ParsePosNonzeroError::ParseInt(_))
|
Err(ParsePosNonzeroError::ParseInt(_)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_negative() {
|
fn test_negative() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_pos_nonzero("-555"),
|
PositiveNonzeroInteger::parse("-555"),
|
||||||
Err(ParsePosNonzeroError::Creation(CreationError::Negative))
|
Err(ParsePosNonzeroError::Creation(CreationError::Negative)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_zero() {
|
fn test_zero() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_pos_nonzero("0"),
|
PositiveNonzeroInteger::parse("0"),
|
||||||
Err(ParsePosNonzeroError::Creation(CreationError::Zero))
|
Err(ParsePosNonzeroError::Creation(CreationError::Zero)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_positive() {
|
fn test_positive() {
|
||||||
let x = PositiveNonzeroInteger::new(42);
|
let x = PositiveNonzeroInteger::new(42).unwrap();
|
||||||
assert!(x.is_ok());
|
assert_eq!(x.0, 42);
|
||||||
assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap()));
|
assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
// This shopping list program isn't compiling! Use your knowledge of generics to
|
// `Vec<T>` is generic over the type `T`. In most cases, the compiler is able to
|
||||||
// fix it.
|
// 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() {
|
||||||
let mut shopping_list: Vec<?> = Vec::new();
|
// TODO: Fix the compiler error by annotating the type of the vector
|
||||||
shopping_list.push("milk");
|
// `Vec<T>`. Choose `T` as some integer type that can be created from
|
||||||
|
// `u8` and `i8`.
|
||||||
|
let mut numbers = Vec::new();
|
||||||
|
|
||||||
|
// Don't change the lines below.
|
||||||
|
let n1: u8 = 42;
|
||||||
|
numbers.push(n1.into());
|
||||||
|
let n2: i8 = -1;
|
||||||
|
numbers.push(n2.into());
|
||||||
|
|
||||||
|
println!("{numbers:?}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
// This powerful wrapper provides the ability to store a positive integer value.
|
// This powerful wrapper provides the ability to store a positive integer value.
|
||||||
// Rewrite it using generics so that it supports wrapping ANY type.
|
// TODO: Rewrite it using a generic so that it supports wrapping ANY type.
|
||||||
|
|
||||||
struct Wrapper {
|
struct Wrapper {
|
||||||
value: u32,
|
value: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Adapt the struct's implementation to be generic over the wrapped value.
|
||||||
impl Wrapper {
|
impl Wrapper {
|
||||||
fn new(value: u32) -> Self {
|
fn new(value: u32) -> Self {
|
||||||
Wrapper { value }
|
Wrapper { value }
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
// Time to implement some traits! Your task is to implement the trait
|
// The trait `AppendBar` has only one function which appends "Bar" to any object
|
||||||
// `AppendBar` for the type `String`. The trait AppendBar has only one function,
|
// implementing this trait.
|
||||||
// which appends "Bar" to any object implementing this trait.
|
|
||||||
|
|
||||||
trait AppendBar {
|
trait AppendBar {
|
||||||
fn append_bar(self) -> Self;
|
fn append_bar(self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppendBar for String {
|
impl AppendBar for String {
|
||||||
// TODO: Implement `AppendBar` for type `String`.
|
// TODO: Implement `AppendBar` for the type `String`.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let s = String::from("Foo");
|
let s = String::from("Foo");
|
||||||
let s = s.append_bar();
|
let s = s.append_bar();
|
||||||
println!("s: {}", s);
|
println!("s: {s}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -22,14 +20,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_foo_bar() {
|
fn is_foo_bar() {
|
||||||
assert_eq!(String::from("Foo").append_bar(), String::from("FooBar"));
|
assert_eq!(String::from("Foo").append_bar(), "FooBar");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_bar_bar() {
|
fn is_bar_bar() {
|
||||||
assert_eq!(
|
assert_eq!(String::from("").append_bar().append_bar(), "BarBar");
|
||||||
String::from("").append_bar().append_bar(),
|
|
||||||
String::from("BarBar")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
// Your task is to implement the trait `AppendBar` for a vector of strings. To
|
|
||||||
// implement this trait, consider for a moment what it means to 'append "Bar"'
|
|
||||||
// to a vector of strings.
|
|
||||||
//
|
|
||||||
// No boiler plate code this time, you can do this!
|
|
||||||
|
|
||||||
trait AppendBar {
|
trait AppendBar {
|
||||||
fn append_bar(self) -> Self;
|
fn append_bar(self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement trait `AppendBar` for a vector of strings.
|
// TODO: Implement the trait `AppendBar` for a vector of strings.
|
||||||
|
// `appned_bar` should push the string "Bar" into the vector.
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// You can optionally experiment here.
|
// You can optionally experiment here.
|
||||||
|
@ -21,7 +16,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn is_vec_pop_eq_bar() {
|
fn is_vec_pop_eq_bar() {
|
||||||
let mut foo = vec![String::from("Foo")].append_bar();
|
let mut foo = vec![String::from("Foo")].append_bar();
|
||||||
assert_eq!(foo.pop().unwrap(), String::from("Bar"));
|
assert_eq!(foo.pop().unwrap(), "Bar");
|
||||||
assert_eq!(foo.pop().unwrap(), String::from("Foo"));
|
assert_eq!(foo.pop().unwrap(), "Foo");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
// Your task is to implement the Licensed trait for both structures and have
|
|
||||||
// them return the same information without writing the same function twice.
|
|
||||||
//
|
|
||||||
// Consider what you can add to the Licensed trait.
|
|
||||||
|
|
||||||
trait Licensed {
|
trait Licensed {
|
||||||
|
// TODO: Add a default implementation for `licensing_info` so that
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +14,8 @@ struct OtherSoftware {
|
||||||
version_number: String,
|
version_number: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Licensed for SomeSoftware {} // Don't edit this line
|
impl Licensed for SomeSoftware {} // Don't edit this line.
|
||||||
impl Licensed for OtherSoftware {} // Don't edit this line
|
impl Licensed for OtherSoftware {} // Don't edit this line.
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// You can optionally experiment here.
|
// You can optionally experiment here.
|
||||||
|
@ -28,7 +27,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_licensing_info_the_same() {
|
fn is_licensing_info_the_same() {
|
||||||
let licensing_info = String::from("Some information");
|
let licensing_info = "Default license";
|
||||||
let some_software = SomeSoftware { version_number: 1 };
|
let some_software = SomeSoftware { version_number: 1 };
|
||||||
let other_software = OtherSoftware {
|
let other_software = OtherSoftware {
|
||||||
version_number: "v2.0.0".to_string(),
|
version_number: "v2.0.0".to_string(),
|
||||||
|
|
|
@ -1,23 +1,18 @@
|
||||||
// Your task is to replace the '??' sections so the code compiles.
|
|
||||||
//
|
|
||||||
// Don't change any line other than the marked one.
|
|
||||||
|
|
||||||
trait Licensed {
|
trait Licensed {
|
||||||
fn licensing_info(&self) -> String {
|
fn licensing_info(&self) -> String {
|
||||||
"some information".to_string()
|
"Default license".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SomeSoftware {}
|
struct SomeSoftware;
|
||||||
|
struct OtherSoftware;
|
||||||
struct OtherSoftware {}
|
|
||||||
|
|
||||||
impl Licensed for SomeSoftware {}
|
impl Licensed for SomeSoftware {}
|
||||||
impl Licensed for OtherSoftware {}
|
impl Licensed for OtherSoftware {}
|
||||||
|
|
||||||
// YOU MAY ONLY CHANGE THE NEXT LINE
|
// TODO: Fix the compiler error by only changing the signature of this function.
|
||||||
fn compare_license_types(software: ??, software_two: ??) -> bool {
|
fn compare_license_types(software1: ???, software2: ???) -> bool {
|
||||||
software.licensing_info() == software_two.licensing_info()
|
software1.licensing_info() == software2.licensing_info()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -30,17 +25,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_license_information() {
|
fn compare_license_information() {
|
||||||
let some_software = SomeSoftware {};
|
assert!(compare_license_types(SomeSoftware, OtherSoftware));
|
||||||
let other_software = OtherSoftware {};
|
|
||||||
|
|
||||||
assert!(compare_license_types(some_software, other_software));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compare_license_information_backwards() {
|
fn compare_license_information_backwards() {
|
||||||
let some_software = SomeSoftware {};
|
assert!(compare_license_types(OtherSoftware, SomeSoftware));
|
||||||
let other_software = OtherSoftware {};
|
|
||||||
|
|
||||||
assert!(compare_license_types(other_software, some_software));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
// Your task is to replace the '??' sections so the code compiles.
|
|
||||||
//
|
|
||||||
// Don't change any line other than the marked one.
|
|
||||||
|
|
||||||
trait SomeTrait {
|
trait SomeTrait {
|
||||||
fn some_function(&self) -> bool {
|
fn some_function(&self) -> bool {
|
||||||
true
|
true
|
||||||
|
@ -14,20 +10,30 @@ trait OtherTrait {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SomeStruct {}
|
struct SomeStruct;
|
||||||
struct OtherStruct {}
|
|
||||||
|
|
||||||
impl SomeTrait for SomeStruct {}
|
impl SomeTrait for SomeStruct {}
|
||||||
impl OtherTrait for SomeStruct {}
|
impl OtherTrait for SomeStruct {}
|
||||||
|
|
||||||
|
struct OtherStruct;
|
||||||
impl SomeTrait for OtherStruct {}
|
impl SomeTrait for OtherStruct {}
|
||||||
impl OtherTrait for OtherStruct {}
|
impl OtherTrait for OtherStruct {}
|
||||||
|
|
||||||
// YOU MAY ONLY CHANGE THE NEXT LINE
|
// TODO: Fix the compiler error by only changing the signature of this function.
|
||||||
fn some_func(item: ??) -> bool {
|
fn some_func(item: ???) -> bool {
|
||||||
item.some_function() && item.other_function()
|
item.some_function() && item.other_function()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
some_func(SomeStruct {});
|
// You can optionally experiment here.
|
||||||
some_func(OtherStruct {});
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_some_func() {
|
||||||
|
assert!(some_func(SomeStruct));
|
||||||
|
assert!(some_func(OtherStruct));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// going out of scope before it is used. Remember, references are borrows and do
|
// going out of scope before it is used. Remember, references are borrows and do
|
||||||
// not own their own data. What if their owner goes out of scope?
|
// not own their own data. What if their owner goes out of scope?
|
||||||
|
|
||||||
|
// TODO: Fix the compiler error by updating the function signature.
|
||||||
fn longest(x: &str, y: &str) -> &str {
|
fn longest(x: &str, y: &str) -> &str {
|
||||||
if x.len() > y.len() {
|
if x.len() > y.len() {
|
||||||
x
|
x
|
||||||
|
@ -12,9 +13,16 @@ fn longest(x: &str, y: &str) -> &str {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let string1 = String::from("abcd");
|
// You can optionally experiment here.
|
||||||
let string2 = "xyz";
|
}
|
||||||
|
|
||||||
let result = longest(string1.as_str(), string2);
|
#[cfg(test)]
|
||||||
println!("The longest string is '{}'", result);
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_longest() {
|
||||||
|
assert_eq!(longest("abcd", "123"), "abcd");
|
||||||
|
assert_eq!(longest("abc", "1234"), "1234");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
// So if the compiler is just validating the references passed to the annotated
|
// Don't change this function.
|
||||||
// parameters and the return type, what do we need to change?
|
|
||||||
|
|
||||||
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
||||||
if x.len() > y.len() {
|
if x.len() > y.len() {
|
||||||
x
|
x
|
||||||
|
@ -10,11 +8,13 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// TODO: Fix the compiler error by moving one line.
|
||||||
|
|
||||||
let string1 = String::from("long string is long");
|
let string1 = String::from("long string is long");
|
||||||
let result;
|
let result;
|
||||||
{
|
{
|
||||||
let string2 = String::from("xyz");
|
let string2 = String::from("xyz");
|
||||||
result = longest(string1.as_str(), string2.as_str());
|
result = longest(&string1, &string2);
|
||||||
}
|
}
|
||||||
println!("The longest string is '{}'", result);
|
println!("The longest string is '{result}'");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
// Lifetimes are also needed when structs hold references.
|
// Lifetimes are also needed when structs hold references.
|
||||||
|
|
||||||
|
// TODO: Fix the compiler errors about the struct.
|
||||||
struct Book {
|
struct Book {
|
||||||
author: &str,
|
author: &str,
|
||||||
title: &str,
|
title: &str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let name = String::from("Jill Smith");
|
|
||||||
let title = String::from("Fish Flying");
|
|
||||||
let book = Book {
|
let book = Book {
|
||||||
author: &name,
|
author: "George Orwell",
|
||||||
title: &title,
|
title: "1984",
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("{} by {}", book.title, book.author);
|
println!("{} by {}", book.title, book.author);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// Tests are important to ensure that your code does what you think it should
|
// Tests are important to ensure that your code does what you think it should
|
||||||
// do. Tests can be run on this file with the following command: rustlings run
|
// do.
|
||||||
// tests1
|
|
||||||
//
|
fn is_even(n: i64) -> bool {
|
||||||
// This test has a problem with it -- make the test compile! Make the test pass!
|
n % 2 == 0
|
||||||
// Make the test fail!
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// You can optionally experiment here.
|
// You can optionally experiment here.
|
||||||
|
@ -11,8 +11,13 @@ fn main() {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
// TODO: Import `is_even`. You can use a wildcard to import everything in
|
||||||
|
// the outer module.
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn you_can_assert() {
|
fn you_can_assert() {
|
||||||
|
// TODO: Test the function `is_even` with some values.
|
||||||
|
assert!();
|
||||||
assert!();
|
assert!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
// This test has a problem with it -- make the test compile! Make the test pass!
|
// Calculates the power of 2 using a bit shift.
|
||||||
// Make the test fail!
|
// `1 << n` is equivalent to "2 to the power of n".
|
||||||
|
fn power_of_2(n: u8) -> u64 {
|
||||||
|
1 << n
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// You can optionally experiment here.
|
// You can optionally experiment here.
|
||||||
|
@ -7,8 +10,14 @@ fn main() {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn you_can_assert_eq() {
|
fn you_can_assert_eq() {
|
||||||
|
// TODO: Test the function `power_of_2` with some values.
|
||||||
|
assert_eq!();
|
||||||
|
assert_eq!();
|
||||||
|
assert_eq!();
|
||||||
assert_eq!();
|
assert_eq!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
// This test isn't testing our function -- make it do that in such a way that
|
struct Rectangle {
|
||||||
// the test passes. Then write a second test that tests whether we get the
|
width: i32,
|
||||||
// result we expect to get when we call `is_even(5)`.
|
height: i32,
|
||||||
|
}
|
||||||
|
|
||||||
fn is_even(num: i32) -> bool {
|
impl Rectangle {
|
||||||
num % 2 == 0
|
// Don't change this function.
|
||||||
|
fn new(width: i32, height: i32) -> Self {
|
||||||
|
if width <= 0 || height <= 0 {
|
||||||
|
// Returning a `Result` would be better here. But we want to learn
|
||||||
|
// how to test functions that can panic.
|
||||||
|
panic!("Rectangle width and height can't be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle { width, height }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -15,12 +25,25 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn is_true_when_even() {
|
fn correct_width_and_height() {
|
||||||
assert!();
|
// TODO: This test should check if the rectangle has the size that we
|
||||||
|
// pass to its constructor.
|
||||||
|
let rect = Rectangle::new(10, 20);
|
||||||
|
assert_eq!(???, 10); // Check width
|
||||||
|
assert_eq!(???, 20); // Check height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This test should check if the program panics when we try to create
|
||||||
|
// a rectangle with negative width.
|
||||||
#[test]
|
#[test]
|
||||||
fn is_false_when_odd() {
|
fn negative_width() {
|
||||||
assert!();
|
let _rect = Rectangle::new(-10, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This test should check if the program panics when we try to create
|
||||||
|
// a rectangle with negative height.
|
||||||
|
#[test]
|
||||||
|
fn negative_height() {
|
||||||
|
let _rect = Rectangle::new(10, -10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
// Make sure that we're testing for the correct conditions!
|
|
||||||
|
|
||||||
struct Rectangle {
|
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rectangle {
|
|
||||||
// Only change the test functions themselves
|
|
||||||
fn new(width: i32, height: i32) -> Self {
|
|
||||||
if width <= 0 || height <= 0 {
|
|
||||||
panic!("Rectangle width and height cannot be negative!")
|
|
||||||
}
|
|
||||||
Rectangle { width, height }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// You can optionally experiment here.
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn correct_width_and_height() {
|
|
||||||
// This test should check if the rectangle is the size that we pass into its constructor
|
|
||||||
let rect = Rectangle::new(10, 20);
|
|
||||||
assert_eq!(???, 10); // check width
|
|
||||||
assert_eq!(???, 20); // check height
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn negative_width() {
|
|
||||||
// This test should check if program panics when we try to create rectangle with negative width
|
|
||||||
let _rect = Rectangle::new(-10, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn negative_height() {
|
|
||||||
// This test should check if program panics when we try to create rectangle with negative height
|
|
||||||
let _rect = Rectangle::new(10, -10);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -33,7 +33,7 @@ impl Default for Person {
|
||||||
// 5. Extract the other element from the split operation and parse it into a
|
// 5. Extract the other element from the split operation and parse it into a
|
||||||
// `usize` as the age.
|
// `usize` as the age.
|
||||||
// If while parsing the age, something goes wrong, then return the default of
|
// If while parsing the age, something goes wrong, then return the default of
|
||||||
// Person Otherwise, then return an instantiated Person object with the results
|
// Person. Otherwise, then return an instantiated Person object with the results
|
||||||
|
|
||||||
impl From<&str> for Person {
|
impl From<&str> for Person {
|
||||||
fn from(s: &str) -> Person {}
|
fn from(s: &str) -> Person {}
|
||||||
|
|
|
@ -3,26 +3,27 @@
|
||||||
// - Traits
|
// - Traits
|
||||||
//
|
//
|
||||||
// An imaginary magical school has a new report card generation system written
|
// An imaginary magical school has a new report card generation system written
|
||||||
// in Rust! Currently the system only supports creating report cards where the
|
// 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
|
// 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
|
// school also issues alphabetical grades (A+ -> F-) and needs to be able to
|
||||||
// print both types of report card!
|
// print both types of report card!
|
||||||
//
|
//
|
||||||
// Make the necessary code changes in the struct ReportCard and the impl block
|
// Make the necessary code changes in the struct `ReportCard` and the impl
|
||||||
// to support alphabetical report cards. Change the Grade in the second test to
|
// block to support alphabetical report cards in addition to numerical ones.
|
||||||
// "A+" to show that your changes allow alphabetical grades.
|
|
||||||
|
|
||||||
|
// TODO: Adjust the struct as described above.
|
||||||
struct ReportCard {
|
struct ReportCard {
|
||||||
grade: f32,
|
grade: f32,
|
||||||
student_name: String,
|
student_name: String,
|
||||||
student_age: u8,
|
student_age: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Adjust the impl block as described above.
|
||||||
impl ReportCard {
|
impl ReportCard {
|
||||||
fn print(&self) -> String {
|
fn print(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{} ({}) - achieved a grade of {}",
|
"{} ({}) - achieved a grade of {}",
|
||||||
&self.student_name, &self.student_age, &self.grade
|
&self.student_name, &self.student_age, &self.grade,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,21 +45,20 @@ mod tests {
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
report_card.print(),
|
report_card.print(),
|
||||||
"Tom Wriggle (12) - achieved a grade of 2.1"
|
"Tom Wriggle (12) - achieved a grade of 2.1",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generate_alphabetic_report_card() {
|
fn generate_alphabetic_report_card() {
|
||||||
// TODO: Make sure to change the grade here after you finish the exercise.
|
|
||||||
let report_card = ReportCard {
|
let report_card = ReportCard {
|
||||||
grade: 2.1,
|
grade: "A+",
|
||||||
student_name: "Gary Plotter".to_string(),
|
student_name: "Gary Plotter".to_string(),
|
||||||
student_age: 11,
|
student_age: 11,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
report_card.print(),
|
report_card.print(),
|
||||||
"Gary Plotter (11) - achieved a grade of A+"
|
"Gary Plotter (11) - achieved a grade of A+",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,7 +336,7 @@ What do you think is the more commonly used pattern under Rust developers?
|
||||||
name = "move_semantics1"
|
name = "move_semantics1"
|
||||||
dir = "06_move_semantics"
|
dir = "06_move_semantics"
|
||||||
hint = """
|
hint = """
|
||||||
So you've got the "cannot borrow immutable local variable `vec` as mutable"
|
So you've got the "cannot borrow `vec` as mutable, as it is not declared as mutable"
|
||||||
error on the line where we push an element to the vector, right?
|
error on the line where we push an element to the vector, right?
|
||||||
|
|
||||||
The fix for this is going to be adding one keyword, and the addition is NOT on
|
The fix for this is going to be adding one keyword, and the addition is NOT on
|
||||||
|
@ -372,7 +372,7 @@ name = "move_semantics4"
|
||||||
dir = "06_move_semantics"
|
dir = "06_move_semantics"
|
||||||
hint = """
|
hint = """
|
||||||
Carefully reason about the range in which each mutable reference is in
|
Carefully reason about the range in which each mutable reference is in
|
||||||
scope. Does it help to update the value of referent (`x`) immediately after
|
scope. Does it help to update the value of `x` immediately after
|
||||||
the mutable reference is taken?
|
the mutable reference is taken?
|
||||||
Read more about 'Mutable References' in the book's section 'References and Borrowing':
|
Read more about 'Mutable References' in the book's section 'References and Borrowing':
|
||||||
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.
|
https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.
|
||||||
|
@ -714,17 +714,13 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen
|
||||||
name = "errors6"
|
name = "errors6"
|
||||||
dir = "13_error_handling"
|
dir = "13_error_handling"
|
||||||
hint = """
|
hint = """
|
||||||
This exercise uses a completed version of `PositiveNonzeroInteger` from
|
This exercise uses a completed version of `PositiveNonzeroInteger` from the
|
||||||
errors4.
|
previous exercises.
|
||||||
|
|
||||||
Below the line that `TODO` asks you to change, there is an example of using
|
Below the line that `TODO` asks you to change, there is an example of using
|
||||||
the `map_err()` method on a `Result` to transform one type of error into
|
the `map_err()` method on a `Result` to transform one type of error into
|
||||||
another. Try using something similar on the `Result` from `parse()`. You
|
another. Try using something similar on the `Result` from `parse()`. You
|
||||||
might use the `?` operator to return early from the function, or you might
|
can then use the `?` operator to return early.
|
||||||
use a `match` expression, or maybe there's another way!
|
|
||||||
|
|
||||||
You can create another function inside `impl ParsePosNonzeroError` to use
|
|
||||||
with `map_err()`.
|
|
||||||
|
|
||||||
Read more about `map_err()` in the `std::result` documentation:
|
Read more about `map_err()` in the `std::result` documentation:
|
||||||
https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err"""
|
https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err"""
|
||||||
|
@ -738,19 +734,20 @@ test = false
|
||||||
hint = """
|
hint = """
|
||||||
Vectors in Rust make use of generics to create dynamically sized arrays of any
|
Vectors in Rust make use of generics to create dynamically sized arrays of any
|
||||||
type.
|
type.
|
||||||
|
If the vector `numbers` has the type `Vec<T>`, then we can only push values of
|
||||||
|
type `T` to it. By using `into()` before pushing, we ask the compiler to convert
|
||||||
|
`n1` and `n2` to `T`. But the compiler doesn't know what `T` is yet and needs a
|
||||||
|
type annotation.
|
||||||
|
|
||||||
You need to tell the compiler what type we are pushing onto this vector."""
|
`u8` and `i8` can both be converted to `i16`, `i32` and `i64`. Choose one for
|
||||||
|
the generic of the vector."""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "generics2"
|
name = "generics2"
|
||||||
dir = "14_generics"
|
dir = "14_generics"
|
||||||
hint = """
|
hint = """
|
||||||
Currently we are wrapping only values of type `u32`.
|
Related section in The Book:
|
||||||
|
https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions"""
|
||||||
Maybe we could update the explicit references to this data type somehow?
|
|
||||||
|
|
||||||
If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions
|
|
||||||
"""
|
|
||||||
|
|
||||||
# TRAITS
|
# TRAITS
|
||||||
|
|
||||||
|
@ -758,53 +755,49 @@ If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html
|
||||||
name = "traits1"
|
name = "traits1"
|
||||||
dir = "15_traits"
|
dir = "15_traits"
|
||||||
hint = """
|
hint = """
|
||||||
A discussion about Traits in Rust can be found at:
|
More about traits in The Book:
|
||||||
https://doc.rust-lang.org/book/ch10-02-traits.html
|
https://doc.rust-lang.org/book/ch10-02-traits.html"""
|
||||||
"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "traits2"
|
name = "traits2"
|
||||||
dir = "15_traits"
|
dir = "15_traits"
|
||||||
hint = """
|
hint = """
|
||||||
Notice how the trait takes ownership of `self`, and returns `Self`.
|
Notice how the trait takes ownership of `self` and returns `Self`.
|
||||||
|
|
||||||
Try mutating the incoming string vector. Have a look at the tests to see
|
Although the signature of `append_bar` in the trait takes `self` as argument,
|
||||||
what the result should look like!
|
the implementation can take `mut self` instead. This is possible because the
|
||||||
|
the value is owned anyway."""
|
||||||
Vectors provide suitable methods for adding an element at the end. See
|
|
||||||
the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "traits3"
|
name = "traits3"
|
||||||
dir = "15_traits"
|
dir = "15_traits"
|
||||||
hint = """
|
hint = """
|
||||||
Traits can have a default implementation for functions. Structs that implement
|
Traits can have a default implementation for functions. Data types that
|
||||||
the trait can then use the default version of these functions if they choose not
|
implement the trait can then use the default version of these functions
|
||||||
to implement the function themselves.
|
if they choose not to implement the function themselves.
|
||||||
|
|
||||||
See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations
|
Related section in The Book:
|
||||||
"""
|
https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "traits4"
|
name = "traits4"
|
||||||
dir = "15_traits"
|
dir = "15_traits"
|
||||||
hint = """
|
hint = """
|
||||||
Instead of using concrete types as parameters you can use traits. Try replacing
|
Instead of using concrete types as parameters you can use traits. Try replacing
|
||||||
the '??' with 'impl <what goes here?>'
|
`???` with `impl [what goes here?]`.
|
||||||
|
|
||||||
See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
|
Related section in The Book:
|
||||||
"""
|
https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "traits5"
|
name = "traits5"
|
||||||
dir = "15_traits"
|
dir = "15_traits"
|
||||||
test = false
|
|
||||||
hint = """
|
hint = """
|
||||||
To ensure a parameter implements multiple traits use the '+ syntax'. Try
|
To ensure a parameter implements multiple traits use the '+ syntax'. Try
|
||||||
replacing the '??' with 'impl <> + <>'.
|
replacing `???` with 'impl [what goes here?] + [what goes here?]'.
|
||||||
|
|
||||||
See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax
|
Related section in The Book:
|
||||||
"""
|
https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax"""
|
||||||
|
|
||||||
# QUIZ 3
|
# QUIZ 3
|
||||||
|
|
||||||
|
@ -812,19 +805,24 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#spe
|
||||||
name = "quiz3"
|
name = "quiz3"
|
||||||
dir = "quizzes"
|
dir = "quizzes"
|
||||||
hint = """
|
hint = """
|
||||||
To find the best solution to this challenge you're going to need to think back
|
To find the best solution to this challenge, you need to recall your knowledge
|
||||||
to your knowledge of traits, specifically 'Trait Bound Syntax'
|
of traits, specifically "Trait Bound Syntax":
|
||||||
|
https://doc.rust-lang.org/book/ch10-02-traits.html#trait-bound-syntax
|
||||||
|
|
||||||
You may also need this: `use std::fmt::Display;`."""
|
Here is how to specify a trait bound for an implementation block:
|
||||||
|
`impl<T: Trait1 + Trait2 + …> for Foo<T> { … }`
|
||||||
|
|
||||||
|
You may need this:
|
||||||
|
`use std::fmt::Display;`
|
||||||
|
"""
|
||||||
|
|
||||||
# LIFETIMES
|
# LIFETIMES
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "lifetimes1"
|
name = "lifetimes1"
|
||||||
dir = "16_lifetimes"
|
dir = "16_lifetimes"
|
||||||
test = false
|
|
||||||
hint = """
|
hint = """
|
||||||
Let the compiler guide you. Also take a look at the book if you need help:
|
Let the compiler guide you. Also take a look at The Book if you need help:
|
||||||
https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html"""
|
https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
|
@ -845,9 +843,7 @@ inner block:
|
||||||
name = "lifetimes3"
|
name = "lifetimes3"
|
||||||
dir = "16_lifetimes"
|
dir = "16_lifetimes"
|
||||||
test = false
|
test = false
|
||||||
hint = """
|
hint = """Let the compiler guide you :)"""
|
||||||
If you use a lifetime annotation in a struct's fields, where else does it need
|
|
||||||
to be added?"""
|
|
||||||
|
|
||||||
# TESTS
|
# TESTS
|
||||||
|
|
||||||
|
@ -855,45 +851,31 @@ to be added?"""
|
||||||
name = "tests1"
|
name = "tests1"
|
||||||
dir = "17_tests"
|
dir = "17_tests"
|
||||||
hint = """
|
hint = """
|
||||||
You don't even need to write any code to test -- you can just test values and
|
|
||||||
run that, even though you wouldn't do that in real life. :)
|
|
||||||
|
|
||||||
`assert!` is a macro that needs an argument. Depending on the value of the
|
`assert!` is a macro that needs an argument. Depending on the value of the
|
||||||
argument, `assert!` will do nothing (in which case the test will pass) or
|
argument, `assert!` will do nothing (in which case the test will pass) or
|
||||||
`assert!` will panic (in which case the test will fail).
|
`assert!` will panic (in which case the test will fail).
|
||||||
|
|
||||||
So try giving different values to `assert!` and see which ones compile, which
|
So try giving different values to `assert!` and see which ones compile, which
|
||||||
ones pass, and which ones fail :)"""
|
ones pass, and which ones fail :)
|
||||||
|
|
||||||
|
If you want to check for `false`, you can negate the result of what you're
|
||||||
|
checking using `!`, like `assert!(!…)`."""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "tests2"
|
name = "tests2"
|
||||||
dir = "17_tests"
|
dir = "17_tests"
|
||||||
hint = """
|
hint = """
|
||||||
Like the previous exercise, you don't need to write any code to get this test
|
|
||||||
to compile and run.
|
|
||||||
|
|
||||||
`assert_eq!` is a macro that takes two arguments and compares them. Try giving
|
`assert_eq!` is a macro that takes two arguments and compares them. Try giving
|
||||||
it two values that are equal! Try giving it two arguments that are different!
|
it two values that are equal! Try giving it two arguments that are different!
|
||||||
Try giving it two values that are of different types! Try switching which
|
Try switching which argument comes first and which comes second!"""
|
||||||
argument comes first and which comes second!"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "tests3"
|
name = "tests3"
|
||||||
dir = "17_tests"
|
dir = "17_tests"
|
||||||
hint = """
|
hint = """
|
||||||
You can call a function right where you're passing arguments to `assert!`. So
|
We expect the method `Rectangle::new` to panic for negative values.
|
||||||
you could do something like `assert!(having_fun())`.
|
|
||||||
|
|
||||||
If you want to check that you indeed get `false`, you can negate the result of
|
To handle that, you need to add a special attribute to the test function.
|
||||||
what you're doing using `!`, like `assert!(!having_fun())`."""
|
|
||||||
|
|
||||||
[[exercises]]
|
|
||||||
name = "tests4"
|
|
||||||
dir = "17_tests"
|
|
||||||
hint = """
|
|
||||||
We expect method `Rectangle::new()` to panic for negative values.
|
|
||||||
|
|
||||||
To handle that you need to add a special attribute to the test function.
|
|
||||||
|
|
||||||
You can refer to the docs:
|
You can refer to the docs:
|
||||||
https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
|
https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
|
||||||
|
@ -919,8 +901,7 @@ Step 4:
|
||||||
|
|
||||||
An iterator goes through all elements in a collection, but what if we've run
|
An iterator goes through all elements in a collection, but what if we've run
|
||||||
out of elements? What should we expect here? If you're stuck, take a look at
|
out of elements? What should we expect here? If you're stuck, take a look at
|
||||||
https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas.
|
https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas."""
|
||||||
"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "iterators2"
|
name = "iterators2"
|
||||||
|
@ -1014,8 +995,7 @@ assertions).
|
||||||
|
|
||||||
For a non-empty list keep in mind that we want to use our `Cons` "list builder".
|
For a non-empty list keep in mind that we want to use our `Cons` "list builder".
|
||||||
Although the current list is one of integers (`i32`), feel free to change the
|
Although the current list is one of integers (`i32`), feel free to change the
|
||||||
definition and try other types!
|
definition and try other types!"""
|
||||||
"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "rc1"
|
name = "rc1"
|
||||||
|
@ -1032,8 +1012,7 @@ In the end the `Sun` only has one reference again, to itself.
|
||||||
|
|
||||||
See more at: https://doc.rust-lang.org/book/ch15-04-rc.html
|
See more at: https://doc.rust-lang.org/book/ch15-04-rc.html
|
||||||
|
|
||||||
* Unfortunately Pluto is no longer considered a planet :(
|
* Unfortunately Pluto is no longer considered a planet :("""
|
||||||
"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "arc1"
|
name = "arc1"
|
||||||
|
@ -1048,10 +1027,9 @@ inside the loop but still in the main thread.
|
||||||
thread-local copy of the numbers.
|
thread-local copy of the numbers.
|
||||||
|
|
||||||
This is a simple exercise if you understand the underlying concepts, but if this
|
This is a simple exercise if you understand the underlying concepts, but if this
|
||||||
is too much of a struggle, consider reading through all of Chapter 16 in the
|
is too much of a struggle, consider reading through all of Chapter 16 in The
|
||||||
book:
|
Book:
|
||||||
https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html
|
https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html"""
|
||||||
"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "cow1"
|
name = "cow1"
|
||||||
|
@ -1061,8 +1039,7 @@ If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is
|
||||||
called.
|
called.
|
||||||
|
|
||||||
Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation
|
Check out https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation
|
||||||
on the `Cow` type.
|
on the `Cow` type."""
|
||||||
"""
|
|
||||||
|
|
||||||
# THREADS
|
# THREADS
|
||||||
|
|
||||||
|
@ -1081,8 +1058,7 @@ https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-f
|
||||||
Use the `JoinHandle`s to wait for each thread to finish and collect their
|
Use the `JoinHandle`s to wait for each thread to finish and collect their
|
||||||
results.
|
results.
|
||||||
|
|
||||||
https://doc.rust-lang.org/std/thread/struct.JoinHandle.html
|
https://doc.rust-lang.org/std/thread/struct.JoinHandle.html"""
|
||||||
"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "threads2"
|
name = "threads2"
|
||||||
|
@ -1103,8 +1079,7 @@ let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
|
||||||
```
|
```
|
||||||
|
|
||||||
Similar to the code in the following example in the book:
|
Similar to the code in the following example in the book:
|
||||||
https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads
|
https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads"""
|
||||||
"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "threads3"
|
name = "threads3"
|
||||||
|
@ -1119,8 +1094,7 @@ one thread and receive them in another.
|
||||||
Multiple producers are possible by using clone() to create a duplicate of the
|
Multiple producers are possible by using clone() to create a duplicate of the
|
||||||
original sending end.
|
original sending end.
|
||||||
|
|
||||||
See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info.
|
See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info."""
|
||||||
"""
|
|
||||||
|
|
||||||
# MACROS
|
# MACROS
|
||||||
|
|
||||||
|
@ -1236,8 +1210,7 @@ or a closure to wrap the error from `parse::<usize>`.
|
||||||
|
|
||||||
Yet another hint: If you would like to propagate errors by using the `?`
|
Yet another hint: If you would like to propagate errors by using the `?`
|
||||||
operator in your solution, you might want to look at
|
operator in your solution, you might want to look at
|
||||||
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html
|
https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html"""
|
||||||
"""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "try_from_into"
|
name = "try_from_into"
|
||||||
|
|
|
@ -1 +1,92 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
// Using catch-all error types like `Box<dyn Error>` isn't recommended for
|
||||||
|
// library code where callers might want to make decisions based on the error
|
||||||
|
// content instead of printing it out or propagating it further. Here, we define
|
||||||
|
// a custom error type to make it possible for callers to decide what to do next
|
||||||
|
// when our function returns an error.
|
||||||
|
|
||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum CreationError {
|
||||||
|
Negative,
|
||||||
|
Zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
enum ParsePosNonzeroError {
|
||||||
|
Creation(CreationError),
|
||||||
|
ParseInt(ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParsePosNonzeroError {
|
||||||
|
fn from_creation(err: CreationError) -> Self {
|
||||||
|
Self::Creation(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_parseint(err: ParseIntError) -> Self {
|
||||||
|
Self::ParseInt(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
|
impl PositiveNonzeroInteger {
|
||||||
|
fn new(value: i64) -> Result<Self, CreationError> {
|
||||||
|
match value {
|
||||||
|
x if x < 0 => Err(CreationError::Negative),
|
||||||
|
x if x == 0 => Err(CreationError::Zero),
|
||||||
|
x => Ok(Self(x as u64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {
|
||||||
|
// Return an appropriate error instead of panicking when `parse()`
|
||||||
|
// returns an error.
|
||||||
|
let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parseint)?;
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
Self::new(x).map_err(ParsePosNonzeroError::from_creation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use std::num::IntErrorKind;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_error() {
|
||||||
|
assert!(matches!(
|
||||||
|
PositiveNonzeroInteger::parse("not a number"),
|
||||||
|
Err(ParsePosNonzeroError::ParseInt(_)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_negative() {
|
||||||
|
assert_eq!(
|
||||||
|
PositiveNonzeroInteger::parse("-555"),
|
||||||
|
Err(ParsePosNonzeroError::Creation(CreationError::Negative)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero() {
|
||||||
|
assert_eq!(
|
||||||
|
PositiveNonzeroInteger::parse("0"),
|
||||||
|
Err(ParsePosNonzeroError::Creation(CreationError::Zero)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_positive() {
|
||||||
|
let x = PositiveNonzeroInteger::new(42).unwrap();
|
||||||
|
assert_eq!(x.0, 42);
|
||||||
|
assert_eq!(PositiveNonzeroInteger::parse("42"), Ok(x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,17 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
// `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() {
|
||||||
|
// `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:?}");
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,28 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,32 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
// 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,27 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,36 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,35 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,39 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,28 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
// The Rust compiler needs to know how to check whether supplied references are
|
||||||
|
// valid, so that it can let the programmer know if a reference is at risk of
|
||||||
|
// going out of scope before it is used. Remember, references are borrows and do
|
||||||
|
// not own their own data. What if their owner goes out of scope?
|
||||||
|
|
||||||
|
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
||||||
|
// ^^^^ ^^ ^^ ^^
|
||||||
|
if x.len() > y.len() {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_longest() {
|
||||||
|
assert_eq!(longest("abcd", "123"), "abcd");
|
||||||
|
assert_eq!(longest("abc", "1234"), "1234");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,33 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
||||||
|
if x.len() > y.len() {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let string1 = String::from("long string is long");
|
||||||
|
// Solution1: You can move `strings2` out of the inner block so that it is
|
||||||
|
// not dropped before the print statement.
|
||||||
|
let string2 = String::from("xyz");
|
||||||
|
let result;
|
||||||
|
{
|
||||||
|
result = longest(&string1, &string2);
|
||||||
|
}
|
||||||
|
println!("The longest string is '{result}'");
|
||||||
|
// `string2` dropped at the end of the function.
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
let string1 = String::from("long string is long");
|
||||||
|
let result;
|
||||||
|
{
|
||||||
|
let string2 = String::from("xyz");
|
||||||
|
result = longest(&string1, &string2);
|
||||||
|
// Solution2: You can move the print statement into the inner block so
|
||||||
|
// that it is executed before `string2` is dropped.
|
||||||
|
println!("The longest string is '{result}'");
|
||||||
|
// `string2` dropped here (end of the inner scope).
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,18 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
// Lifetimes are also needed when structs hold references.
|
||||||
|
|
||||||
|
struct Book<'a> {
|
||||||
|
// ^^^^ added a lifetime annotation
|
||||||
|
author: &'a str,
|
||||||
|
// ^^
|
||||||
|
title: &'a str,
|
||||||
|
// ^^
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let book = Book {
|
||||||
|
author: "George Orwell",
|
||||||
|
title: "1984",
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{} by {}", book.title, book.author);
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,24 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
// Tests are important to ensure that your code does what you think it should
|
||||||
|
// do.
|
||||||
|
|
||||||
|
fn is_even(n: i64) -> bool {
|
||||||
|
n % 2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
// When writing unit tests, it is common to import everything from the outer
|
||||||
|
// module (`super`) using a wildcard.
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn you_can_assert() {
|
||||||
|
assert!(is_even(0));
|
||||||
|
assert!(!is_even(-1));
|
||||||
|
// ^ You can assert `false` using the negation operator `!`.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,22 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
// Calculates the power of 2 using a bit shift.
|
||||||
|
// `1 << n` is equivalent to "2 to the power of n".
|
||||||
|
fn power_of_2(n: u8) -> u64 {
|
||||||
|
1 << n
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn you_can_assert_eq() {
|
||||||
|
assert_eq!(power_of_2(0), 1);
|
||||||
|
assert_eq!(power_of_2(1), 2);
|
||||||
|
assert_eq!(power_of_2(2), 4);
|
||||||
|
assert_eq!(power_of_2(3), 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,45 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
struct Rectangle {
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rectangle {
|
||||||
|
// Don't change this function.
|
||||||
|
fn new(width: i32, height: i32) -> Self {
|
||||||
|
if width <= 0 || height <= 0 {
|
||||||
|
// Returning a `Result` would be better here. But we want to learn
|
||||||
|
// how to test functions that can panic.
|
||||||
|
panic!("Rectangle width and height can't be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle { width, height }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// You can optionally experiment here.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn correct_width_and_height() {
|
||||||
|
let rect = Rectangle::new(10, 20);
|
||||||
|
assert_eq!(rect.width, 10); // Check width
|
||||||
|
assert_eq!(rect.height, 20); // Check height
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic] // Added this attribute to check that the test panics.
|
||||||
|
fn negative_width() {
|
||||||
|
let _rect = Rectangle::new(-10, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic] // Added this attribute to check that the test panics.
|
||||||
|
fn negative_height() {
|
||||||
|
let _rect = Rectangle::new(10, -10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
|
|
@ -1 +1,69 @@
|
||||||
// Solutions will be available before the stable release. Thank you for testing the beta version 🥰
|
// This quiz tests:
|
||||||
|
// - Generics
|
||||||
|
// - Traits
|
||||||
|
//
|
||||||
|
// 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+",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue