mirror of https://github.com/rust-lang/rustlings
Merge branch '5.0-dev'
commit
c791cf4232
@ -0,0 +1,20 @@
|
|||||||
|
name: Rustlings Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --verbose
|
||||||
|
- name: Run tests
|
||||||
|
run: cargo test --verbose
|
@ -1,98 +0,0 @@
|
|||||||
// advanced_errs1.rs
|
|
||||||
|
|
||||||
// Remember back in errors6, we had multiple mapping functions so that we
|
|
||||||
// could translate lower-level errors into our custom error type using
|
|
||||||
// `map_err()`? What if we could use the `?` operator directly instead?
|
|
||||||
|
|
||||||
// Make this code compile! Execute `rustlings hint advanced_errs1` for
|
|
||||||
// hints :)
|
|
||||||
|
|
||||||
// I AM NOT DONE
|
|
||||||
|
|
||||||
use std::num::ParseIntError;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
// This is a custom error type that we will be using in the `FromStr`
|
|
||||||
// implementation.
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
enum ParsePosNonzeroError {
|
|
||||||
Creation(CreationError),
|
|
||||||
ParseInt(ParseIntError),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CreationError> for ParsePosNonzeroError {
|
|
||||||
fn from(e: CreationError) -> Self {
|
|
||||||
// TODO: complete this implementation so that the `?` operator will
|
|
||||||
// work for `CreationError`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: implement another instance of the `From` trait here so that the
|
|
||||||
// `?` operator will work in the other place in the `FromStr`
|
|
||||||
// implementation below.
|
|
||||||
|
|
||||||
// Don't change anything below this line.
|
|
||||||
|
|
||||||
impl FromStr for PositiveNonzeroInteger {
|
|
||||||
type Err = ParsePosNonzeroError;
|
|
||||||
fn from_str(s: &str) -> Result<PositiveNonzeroInteger, Self::Err> {
|
|
||||||
let x: i64 = s.parse()?;
|
|
||||||
Ok(PositiveNonzeroInteger::new(x)?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
struct PositiveNonzeroInteger(u64);
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
|
||||||
enum CreationError {
|
|
||||||
Negative,
|
|
||||||
Zero,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PositiveNonzeroInteger {
|
|
||||||
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
|
|
||||||
match value {
|
|
||||||
x if x < 0 => Err(CreationError::Negative),
|
|
||||||
x if x == 0 => Err(CreationError::Zero),
|
|
||||||
x => Ok(PositiveNonzeroInteger(x as u64)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_parse_error() {
|
|
||||||
// We can't construct a ParseIntError, so we have to pattern match.
|
|
||||||
assert!(matches!(
|
|
||||||
PositiveNonzeroInteger::from_str("not a number"),
|
|
||||||
Err(ParsePosNonzeroError::ParseInt(_))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_negative() {
|
|
||||||
assert_eq!(
|
|
||||||
PositiveNonzeroInteger::from_str("-555"),
|
|
||||||
Err(ParsePosNonzeroError::Creation(CreationError::Negative))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_zero() {
|
|
||||||
assert_eq!(
|
|
||||||
PositiveNonzeroInteger::from_str("0"),
|
|
||||||
Err(ParsePosNonzeroError::Creation(CreationError::Zero))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_positive() {
|
|
||||||
let x = PositiveNonzeroInteger::new(42);
|
|
||||||
assert!(x.is_ok());
|
|
||||||
assert_eq!(PositiveNonzeroInteger::from_str("42"), Ok(x.unwrap()));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,202 +0,0 @@
|
|||||||
// advanced_errs2.rs
|
|
||||||
|
|
||||||
// This exercise demonstrates a few traits that are useful for custom error
|
|
||||||
// types to implement, especially so that other code can consume the custom
|
|
||||||
// error type more usefully.
|
|
||||||
|
|
||||||
// Make this compile, and make the tests pass!
|
|
||||||
// Execute `rustlings hint advanced_errs2` for hints.
|
|
||||||
|
|
||||||
// Steps:
|
|
||||||
// 1. Implement a missing trait so that `main()` will compile.
|
|
||||||
// 2. Complete the partial implementation of `From` for
|
|
||||||
// `ParseClimateError`.
|
|
||||||
// 3. Handle the missing error cases in the `FromStr` implementation for
|
|
||||||
// `Climate`.
|
|
||||||
// 4. Complete the partial implementation of `Display` for
|
|
||||||
// `ParseClimateError`.
|
|
||||||
|
|
||||||
// I AM NOT DONE
|
|
||||||
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
use std::num::{ParseFloatError, ParseIntError};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
// This is the custom error type that we will be using for the parser for
|
|
||||||
// `Climate`.
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
enum ParseClimateError {
|
|
||||||
Empty,
|
|
||||||
BadLen,
|
|
||||||
NoCity,
|
|
||||||
ParseInt(ParseIntError),
|
|
||||||
ParseFloat(ParseFloatError),
|
|
||||||
}
|
|
||||||
|
|
||||||
// This `From` implementation allows the `?` operator to work on
|
|
||||||
// `ParseIntError` values.
|
|
||||||
impl From<ParseIntError> for ParseClimateError {
|
|
||||||
fn from(e: ParseIntError) -> Self {
|
|
||||||
Self::ParseInt(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This `From` implementation allows the `?` operator to work on
|
|
||||||
// `ParseFloatError` values.
|
|
||||||
impl From<ParseFloatError> for ParseClimateError {
|
|
||||||
fn from(e: ParseFloatError) -> Self {
|
|
||||||
// TODO: Complete this function
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement a missing trait so that `main()` below will compile. It
|
|
||||||
// is not necessary to implement any methods inside the missing trait.
|
|
||||||
|
|
||||||
// The `Display` trait allows for other code to obtain the error formatted
|
|
||||||
// as a user-visible string.
|
|
||||||
impl Display for ParseClimateError {
|
|
||||||
// TODO: Complete this function so that it produces the correct strings
|
|
||||||
// for each error variant.
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
// Imports the variants to make the following code more compact.
|
|
||||||
use ParseClimateError::*;
|
|
||||||
match self {
|
|
||||||
NoCity => write!(f, "no city name"),
|
|
||||||
ParseFloat(e) => write!(f, "error parsing temperature: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
struct Climate {
|
|
||||||
city: String,
|
|
||||||
year: u32,
|
|
||||||
temp: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parser for `Climate`.
|
|
||||||
// 1. Split the input string into 3 fields: city, year, temp.
|
|
||||||
// 2. Return an error if the string is empty or has the wrong number of
|
|
||||||
// fields.
|
|
||||||
// 3. Return an error if the city name is empty.
|
|
||||||
// 4. Parse the year as a `u32` and return an error if that fails.
|
|
||||||
// 5. Parse the temp as a `f32` and return an error if that fails.
|
|
||||||
// 6. Return an `Ok` value containing the completed `Climate` value.
|
|
||||||
impl FromStr for Climate {
|
|
||||||
type Err = ParseClimateError;
|
|
||||||
// TODO: Complete this function by making it handle the missing error
|
|
||||||
// cases.
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let v: Vec<_> = s.split(',').collect();
|
|
||||||
let (city, year, temp) = match &v[..] {
|
|
||||||
[city, year, temp] => (city.to_string(), year, temp),
|
|
||||||
_ => return Err(ParseClimateError::BadLen),
|
|
||||||
};
|
|
||||||
let year: u32 = year.parse()?;
|
|
||||||
let temp: f32 = temp.parse()?;
|
|
||||||
Ok(Climate { city, year, temp })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't change anything below this line (other than to enable ignored
|
|
||||||
// tests).
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
println!("{:?}", "Hong Kong,1999,25.7".parse::<Climate>()?);
|
|
||||||
println!("{:?}", "".parse::<Climate>()?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
#[test]
|
|
||||||
fn test_empty() {
|
|
||||||
let res = "".parse::<Climate>();
|
|
||||||
assert_eq!(res, Err(ParseClimateError::Empty));
|
|
||||||
assert_eq!(res.unwrap_err().to_string(), "empty input");
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_short() {
|
|
||||||
let res = "Boston,1991".parse::<Climate>();
|
|
||||||
assert_eq!(res, Err(ParseClimateError::BadLen));
|
|
||||||
assert_eq!(res.unwrap_err().to_string(), "incorrect number of fields");
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_long() {
|
|
||||||
let res = "Paris,1920,17.2,extra".parse::<Climate>();
|
|
||||||
assert_eq!(res, Err(ParseClimateError::BadLen));
|
|
||||||
assert_eq!(res.unwrap_err().to_string(), "incorrect number of fields");
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_no_city() {
|
|
||||||
let res = ",1997,20.5".parse::<Climate>();
|
|
||||||
assert_eq!(res, Err(ParseClimateError::NoCity));
|
|
||||||
assert_eq!(res.unwrap_err().to_string(), "no city name");
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_parse_int_neg() {
|
|
||||||
let res = "Barcelona,-25,22.3".parse::<Climate>();
|
|
||||||
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
|
|
||||||
let err = res.unwrap_err();
|
|
||||||
if let ParseClimateError::ParseInt(ref inner) = err {
|
|
||||||
assert_eq!(
|
|
||||||
err.to_string(),
|
|
||||||
format!("error parsing year: {}", inner.to_string())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_parse_int_bad() {
|
|
||||||
let res = "Beijing,foo,15.0".parse::<Climate>();
|
|
||||||
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
|
|
||||||
let err = res.unwrap_err();
|
|
||||||
if let ParseClimateError::ParseInt(ref inner) = err {
|
|
||||||
assert_eq!(
|
|
||||||
err.to_string(),
|
|
||||||
format!("error parsing year: {}", inner.to_string())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_parse_float() {
|
|
||||||
let res = "Manila,2001,bar".parse::<Climate>();
|
|
||||||
assert!(matches!(res, Err(ParseClimateError::ParseFloat(_))));
|
|
||||||
let err = res.unwrap_err();
|
|
||||||
if let ParseClimateError::ParseFloat(ref inner) = err {
|
|
||||||
assert_eq!(
|
|
||||||
err.to_string(),
|
|
||||||
format!("error parsing temperature: {}", inner.to_string())
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_parse_good() {
|
|
||||||
let res = "Munich,2015,23.1".parse::<Climate>();
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
Ok(Climate {
|
|
||||||
city: "Munich".to_string(),
|
|
||||||
year: 2015,
|
|
||||||
temp: 23.1,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
fn test_downcast() {
|
|
||||||
let res = "São Paulo,-21,28.5".parse::<Climate>();
|
|
||||||
assert!(matches!(res, Err(ParseClimateError::ParseInt(_))));
|
|
||||||
let err = res.unwrap_err();
|
|
||||||
let inner: Option<&(dyn Error + 'static)> = err.source();
|
|
||||||
assert!(inner.is_some());
|
|
||||||
assert!(inner.unwrap().is::<ParseIntError>());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
mod advanced_errs1;
|
|
||||||
mod advanced_errs2;
|
|
@ -0,0 +1,28 @@
|
|||||||
|
// clippy3.rs
|
||||||
|
// Here's a couple more easy Clippy fixes, so you can see its utility.
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
|
#[allow(unused_variables, unused_assignments)]
|
||||||
|
fn main() {
|
||||||
|
let my_option: Option<()> = None;
|
||||||
|
if my_option.is_none() {
|
||||||
|
my_option.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let my_arr = &[
|
||||||
|
-1, -2, -3
|
||||||
|
-4, -5, -6
|
||||||
|
];
|
||||||
|
println!("My array! Here it is: {:?}", my_arr);
|
||||||
|
|
||||||
|
let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5);
|
||||||
|
println!("This Vec is empty, see? {:?}", my_empty_vec);
|
||||||
|
|
||||||
|
let mut value_a = 45;
|
||||||
|
let mut value_b = 66;
|
||||||
|
// Let's swap these two!
|
||||||
|
value_a = value_b;
|
||||||
|
value_b = value_a;
|
||||||
|
println!("value a: {}; value b: {}", value_a, value_b);
|
||||||
|
}
|
@ -1,2 +0,0 @@
|
|||||||
mod clippy1;
|
|
||||||
mod clippy2;
|
|
@ -1,4 +0,0 @@
|
|||||||
mod hashmap1;
|
|
||||||
mod hashmap2;
|
|
||||||
mod vec1;
|
|
||||||
mod vec2;
|
|
@ -1,5 +0,0 @@
|
|||||||
mod as_ref_mut;
|
|
||||||
mod from_into;
|
|
||||||
mod from_str;
|
|
||||||
mod try_from_into;
|
|
||||||
mod using_as;
|
|
@ -1,3 +0,0 @@
|
|||||||
mod enums1;
|
|
||||||
mod enums2;
|
|
||||||
mod enums3;
|
|
@ -1,6 +0,0 @@
|
|||||||
mod errors1;
|
|
||||||
mod errors2;
|
|
||||||
mod errors3;
|
|
||||||
mod errors4;
|
|
||||||
mod errors5;
|
|
||||||
mod errors6;
|
|
@ -1,5 +0,0 @@
|
|||||||
mod functions1;
|
|
||||||
mod functions2;
|
|
||||||
mod functions3;
|
|
||||||
mod functions4;
|
|
||||||
mod functions5;
|
|
@ -1,58 +0,0 @@
|
|||||||
// 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. Change the Grade in the second test to "A+"
|
|
||||||
// to show that your changes allow alphabetical grades.
|
|
||||||
|
|
||||||
// Execute 'rustlings hint generics3' for hints!
|
|
||||||
|
|
||||||
// I AM NOT DONE
|
|
||||||
|
|
||||||
pub struct ReportCard {
|
|
||||||
pub grade: f32,
|
|
||||||
pub student_name: String,
|
|
||||||
pub student_age: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReportCard {
|
|
||||||
pub fn print(&self) -> String {
|
|
||||||
format!("{} ({}) - achieved a grade of {}",
|
|
||||||
&self.student_name, &self.student_age, &self.grade)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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() {
|
|
||||||
// TODO: Make sure to change the grade here after you finish the exercise.
|
|
||||||
let report_card = ReportCard {
|
|
||||||
grade: 2.1,
|
|
||||||
student_name: "Gary Plotter".to_string(),
|
|
||||||
student_age: 11,
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
report_card.print(),
|
|
||||||
"Gary Plotter (11) - achieved a grade of A+"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
mod generics1;
|
|
||||||
mod generics2;
|
|
||||||
mod generics3;
|
|
@ -0,0 +1,11 @@
|
|||||||
|
# Hashmaps
|
||||||
|
A *hash map* allows you to associate a value with a particular key.
|
||||||
|
You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map),
|
||||||
|
[*dictionary* in Python](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) or an *associative array* in other languages.
|
||||||
|
|
||||||
|
This is the other data structure that we've been talking about before, when
|
||||||
|
talking about Vecs.
|
||||||
|
|
||||||
|
## Further information
|
||||||
|
|
||||||
|
- [Storing Keys with Associated Values in Hash Maps](https://doc.rust-lang.org/book/ch08-03-hash-maps.html)
|
@ -0,0 +1,87 @@
|
|||||||
|
// hashmaps3.rs
|
||||||
|
|
||||||
|
// A list of scores (one per line) of a soccer match is given. Each line
|
||||||
|
// is of the form :
|
||||||
|
// <team_1_name>,<team_2_name>,<team_1_goals>,<team_2_goals>
|
||||||
|
// Example: England,France,4,2 (England scored 4 goals, France 2).
|
||||||
|
|
||||||
|
// You have to build a scores table containing the name of the team, goals
|
||||||
|
// the team scored, and goals the team conceded. One approach to build
|
||||||
|
// the scores table is to use a Hashmap. The solution is partially
|
||||||
|
// written to use a Hashmap, complete it to pass the test.
|
||||||
|
|
||||||
|
// Make me pass the tests!
|
||||||
|
|
||||||
|
// Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a hint.
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
// A structure to store team name and its goal details.
|
||||||
|
struct Team {
|
||||||
|
name: String,
|
||||||
|
goals_scored: u8,
|
||||||
|
goals_conceded: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_scores_table(results: String) -> HashMap<String, Team> {
|
||||||
|
// The name of the team is the key and its associated struct is the value.
|
||||||
|
let mut scores: HashMap<String, Team> = HashMap::new();
|
||||||
|
|
||||||
|
for r in results.lines() {
|
||||||
|
let v: Vec<&str> = r.split(',').collect();
|
||||||
|
let team_1_name = v[0].to_string();
|
||||||
|
let team_1_score: u8 = v[2].parse().unwrap();
|
||||||
|
let team_2_name = v[1].to_string();
|
||||||
|
let team_2_score: u8 = v[3].parse().unwrap();
|
||||||
|
// TODO: Populate the scores table with details extracted from the
|
||||||
|
// current line. Keep in mind that goals scored by team_1
|
||||||
|
// will be number of goals conceded from team_2, and similarly
|
||||||
|
// goals scored by team_2 will be the number of goals conceded by
|
||||||
|
// team_1.
|
||||||
|
}
|
||||||
|
scores
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn get_results() -> String {
|
||||||
|
let results = "".to_string()
|
||||||
|
+ "England,France,4,2\n"
|
||||||
|
+ "France,Italy,3,1\n"
|
||||||
|
+ "Poland,Spain,2,0\n"
|
||||||
|
+ "Germany,England,2,1\n";
|
||||||
|
results
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_scores() {
|
||||||
|
let scores = build_scores_table(get_results());
|
||||||
|
|
||||||
|
let mut keys: Vec<&String> = scores.keys().collect();
|
||||||
|
keys.sort();
|
||||||
|
assert_eq!(
|
||||||
|
keys,
|
||||||
|
vec!["England", "France", "Germany", "Italy", "Poland", "Spain"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_team_score_1() {
|
||||||
|
let scores = build_scores_table(get_results());
|
||||||
|
let team = scores.get("England").unwrap();
|
||||||
|
assert_eq!(team.goals_scored, 5);
|
||||||
|
assert_eq!(team.goals_conceded, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_team_score_2() {
|
||||||
|
let scores = build_scores_table(get_results());
|
||||||
|
let team = scores.get("Spain").unwrap();
|
||||||
|
assert_eq!(team.goals_scored, 0);
|
||||||
|
assert_eq!(team.goals_conceded, 2);
|
||||||
|
}
|
||||||
|
}
|
@ -1,2 +0,0 @@
|
|||||||
mod if1;
|
|
||||||
mod if2;
|
|
@ -1,2 +0,0 @@
|
|||||||
mod intro1;
|
|
||||||
mod intro2;
|
|
@ -0,0 +1,17 @@
|
|||||||
|
# Lifetimes
|
||||||
|
|
||||||
|
Lifetimes tell the compiler how to check whether references live long
|
||||||
|
enough to be valid in any given situation. For example lifetimes say
|
||||||
|
"make sure parameter 'a' lives as long as parameter 'b' so that the return
|
||||||
|
value is valid".
|
||||||
|
|
||||||
|
They are only necessary on borrows, i.e. references,
|
||||||
|
since copied parameters or moves are owned in their scope and cannot
|
||||||
|
be referenced outside. Lifetimes mean that calling code of e.g. functions
|
||||||
|
can be checked to make sure their arguments are valid. Lifetimes are
|
||||||
|
restrictive of their callers.
|
||||||
|
|
||||||
|
## Further information
|
||||||
|
|
||||||
|
- [Validating References with Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html)
|
||||||
|
- [Lifetimes (in Rust By Example)](https://doc.rust-lang.org/stable/rust-by-example/scope/lifetime.html)
|
@ -0,0 +1,26 @@
|
|||||||
|
// lifetimes1.rs
|
||||||
|
//
|
||||||
|
// 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?
|
||||||
|
//
|
||||||
|
// Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a hint.
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
|
fn longest(x: &str, y: &str) -> &str {
|
||||||
|
if x.len() > y.len() {
|
||||||
|
x
|
||||||
|
} else {
|
||||||
|
y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let string1 = String::from("abcd");
|
||||||
|
let string2 = "xyz";
|
||||||
|
|
||||||
|
let result = longest(string1.as_str(), string2);
|
||||||
|
println!("The longest string is {}", result);
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
// lifetimes2.rs
|
||||||
|
//
|
||||||
|
// So if the compiler is just validating the references passed
|
||||||
|
// to the annotated parameters and the return type, what do
|
||||||
|
// we need to change?
|
||||||
|
//
|
||||||
|
// Execute `rustlings hint lifetimes2` or use the `hint` watch subcommand for a hint.
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
|
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");
|
||||||
|
let result;
|
||||||
|
{
|
||||||
|
let string2 = String::from("xyz");
|
||||||
|
result = longest(string1.as_str(), string2.as_str());
|
||||||
|
}
|
||||||
|
println!("The longest string is {}", result);
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
// lifetimes3.rs
|
||||||
|
//
|
||||||
|
// Lifetimes are also needed when structs hold references.
|
||||||
|
//
|
||||||
|
// Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a hint.
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
|
struct Book {
|
||||||
|
author: &str,
|
||||||
|
title: &str,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let name = String::from("Jill Smith");
|
||||||
|
let title = String::from("Fish Flying");
|
||||||
|
let book = Book { author: &name, title: &title };
|
||||||
|
|
||||||
|
println!("{} by {}", book.title, book.author);
|
||||||
|
}
|
@ -1,4 +0,0 @@
|
|||||||
mod macros1;
|
|
||||||
mod macros2;
|
|
||||||
mod macros3;
|
|
||||||
mod macros4;
|
|
@ -1,26 +0,0 @@
|
|||||||
mod advanced_errors;
|
|
||||||
mod clippy;
|
|
||||||
mod collections;
|
|
||||||
mod conversions;
|
|
||||||
mod enums;
|
|
||||||
mod error_handling;
|
|
||||||
mod functions;
|
|
||||||
mod generics;
|
|
||||||
mod r#if;
|
|
||||||
mod intro;
|
|
||||||
mod macros;
|
|
||||||
mod modules;
|
|
||||||
mod move_semantics;
|
|
||||||
mod option;
|
|
||||||
mod primitive_types;
|
|
||||||
mod quiz1;
|
|
||||||
mod quiz2;
|
|
||||||
mod quiz3;
|
|
||||||
mod quiz4;
|
|
||||||
mod standard_library_types;
|
|
||||||
mod strings;
|
|
||||||
mod structs;
|
|
||||||
mod tests;
|
|
||||||
mod threads;
|
|
||||||
mod traits;
|
|
||||||
mod variables;
|
|
@ -1,3 +0,0 @@
|
|||||||
mod modules1;
|
|
||||||
mod modules2;
|
|
||||||
mod modules3;
|
|
@ -1,6 +0,0 @@
|
|||||||
mod move_semantics1;
|
|
||||||
mod move_semantics2;
|
|
||||||
mod move_semantics3;
|
|
||||||
mod move_semantics4;
|
|
||||||
mod move_semantics5;
|
|
||||||
mod move_semantics6;
|
|
@ -1,3 +0,0 @@
|
|||||||
mod option1;
|
|
||||||
mod option2;
|
|
||||||
mod option3;
|
|
@ -1,23 +0,0 @@
|
|||||||
// option1.rs
|
|
||||||
// Make me compile! Execute `rustlings hint option1` for hints
|
|
||||||
|
|
||||||
// I AM NOT DONE
|
|
||||||
|
|
||||||
// you can modify anything EXCEPT for this function's signature
|
|
||||||
fn print_number(maybe_number: Option<u16>) {
|
|
||||||
println!("printing: {}", maybe_number.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
print_number(13);
|
|
||||||
print_number(99);
|
|
||||||
|
|
||||||
let mut numbers: [Option<u16>; 5];
|
|
||||||
for iter in 0..5 {
|
|
||||||
let number_to_add: u16 = {
|
|
||||||
((iter * 1235) + 2) / (4 * 16)
|
|
||||||
};
|
|
||||||
|
|
||||||
numbers[iter as usize] = number_to_add;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
# Option
|
# Options
|
||||||
|
|
||||||
Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not.
|
Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not.
|
||||||
Option types are very common in Rust code, as they have a number of uses:
|
Option types are very common in Rust code, as they have a number of uses:
|
@ -0,0 +1,37 @@
|
|||||||
|
// options1.rs
|
||||||
|
// Execute `rustlings hint options1` or use the `hint` watch subcommand for a hint.
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
|
// you can modify anything EXCEPT for this function's signature
|
||||||
|
fn print_number(maybe_number: Option<u16>) {
|
||||||
|
println!("printing: {}", maybe_number.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function returns how much icecream there is left in the fridge.
|
||||||
|
// If it's before 10PM, there's 5 pieces left. At 10PM, someone eats them
|
||||||
|
// all, so there'll be no more left :(
|
||||||
|
// TODO: Return an Option!
|
||||||
|
fn maybe_icecream(time_of_day: u16) -> Option<u16> {
|
||||||
|
// We use the 24-hour system here, so 10PM is a value of 22
|
||||||
|
???
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_icecream() {
|
||||||
|
assert_eq!(maybe_icecream(10), Some(5));
|
||||||
|
assert_eq!(maybe_icecream(23), None);
|
||||||
|
assert_eq!(maybe_icecream(22), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn raw_value() {
|
||||||
|
// TODO: Fix this test. How do you get at the value contained in the Option?
|
||||||
|
let icecreams = maybe_icecream(12);
|
||||||
|
assert_eq!(icecreams, 5);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
// option2.rs
|
// options2.rs
|
||||||
// Make me compile! Execute `rustlings hint option2` for hints
|
// Execute `rustlings hint options2` or use the `hint` watch subcommand for a hint.
|
||||||
|
|
||||||
// I AM NOT DONE
|
// I AM NOT DONE
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
// option3.rs
|
// options3.rs
|
||||||
// Make me compile! Execute `rustlings hint option3` for hints
|
// Execute `rustlings hint options3` or use the `hint` watch subcommand for a hint.
|
||||||
|
|
||||||
// I AM NOT DONE
|
// I AM NOT DONE
|
||||||
|
|
@ -1,6 +0,0 @@
|
|||||||
mod primitive_types1;
|
|
||||||
mod primitive_types2;
|
|
||||||
mod primitive_types3;
|
|
||||||
mod primitive_types4;
|
|
||||||
mod primitive_types5;
|
|
||||||
mod primitive_types6;
|
|
@ -1,30 +1,62 @@
|
|||||||
// quiz2.rs
|
// quiz2.rs
|
||||||
// This is a quiz for the following sections:
|
// This is a quiz for the following sections:
|
||||||
// - Strings
|
// - Strings
|
||||||
|
// - Vecs
|
||||||
|
// - Move semantics
|
||||||
|
// - Modules
|
||||||
|
// - Enums
|
||||||
|
|
||||||
// Ok, here are a bunch of values-- some are `String`s, some are `&str`s. Your
|
// Let's build a little machine in form of a function.
|
||||||
// task is to call one of these two functions on each value depending on what
|
// As input, we're going to give a list of strings and commands. These commands
|
||||||
// you think each value is. That is, add either `string_slice` or `string`
|
// determine what action is going to be applied to the string. It can either be:
|
||||||
// before the parentheses on each line. If you're right, it will compile!
|
// - Uppercase the string
|
||||||
|
// - Trim the string
|
||||||
|
// - Append "bar" to the string a specified amount of times
|
||||||
|
// The exact form of this will be:
|
||||||
|
// - The input is going to be a Vector of a 2-length tuple,
|
||||||
|
// the first element is the string, the second one is the command.
|
||||||
|
// - The output element is going to be a Vector of strings.
|
||||||
|
// Execute `rustlings hint quiz2` or use the `hint` watch subcommand for a hint.
|
||||||
|
|
||||||
// I AM NOT DONE
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn string_slice(arg: &str) {
|
pub enum Command {
|
||||||
println!("{}", arg);
|
Uppercase,
|
||||||
|
Trim,
|
||||||
|
Append(usize),
|
||||||
}
|
}
|
||||||
fn string(arg: String) {
|
|
||||||
println!("{}", arg);
|
mod my_module {
|
||||||
|
use super::Command;
|
||||||
|
|
||||||
|
// TODO: Complete the function signature!
|
||||||
|
pub fn transformer(input: ???) -> ??? {
|
||||||
|
// TODO: Complete the output declaration!
|
||||||
|
let mut output: ??? = vec![];
|
||||||
|
for (string, command) in input.iter() {
|
||||||
|
// TODO: Complete the function body. You can do it!
|
||||||
|
}
|
||||||
|
output
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
// TODO: What to we have to import to have `transformer` in scope?
|
||||||
|
use ???;
|
||||||
|
use super::Command;
|
||||||
|
|
||||||
fn main() {
|
#[test]
|
||||||
???("blue");
|
fn it_works() {
|
||||||
???("red".to_string());
|
let output = transformer(vec![
|
||||||
???(String::from("hi"));
|
("hello".into(), Command::Uppercase),
|
||||||
???("rust is fun!".to_owned());
|
(" all roads lead to rome! ".into(), Command::Trim),
|
||||||
???("nice weather".into());
|
("foo".into(), Command::Append(1)),
|
||||||
???(format!("Interpolation {}", "Station"));
|
("bar".into(), Command::Append(5)),
|
||||||
???(&String::from("abc")[0..1]);
|
]);
|
||||||
???(" hello there ".trim());
|
assert_eq!(output[0], "HELLO");
|
||||||
???("Happy Monday!".to_string().replace("Mon", "Tues"));
|
assert_eq!(output[1], "all roads lead to rome!");
|
||||||
???("mY sHiFt KeY iS sTiCkY".to_lowercase());
|
assert_eq!(output[2], "foobar");
|
||||||
|
assert_eq!(output[3], "barbarbarbarbarbar");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
// quiz4.rs
|
|
||||||
// This quiz covers the sections:
|
|
||||||
// - Modules
|
|
||||||
// - Macros
|
|
||||||
|
|
||||||
// Write a macro that passes the quiz! No hints this time, you can do it!
|
|
||||||
|
|
||||||
// I AM NOT DONE
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_my_macro_world() {
|
|
||||||
assert_eq!(my_macro!("world!"), "Hello world!");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_my_macro_goodbye() {
|
|
||||||
assert_eq!(my_macro!("goodbye!"), "Hello goodbye!");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
mod arc1;
|
|
||||||
mod box1;
|
|
||||||
mod iterators1;
|
|
||||||
mod iterators2;
|
|
||||||
mod iterators3;
|
|
||||||
mod iterators4;
|
|
||||||
mod iterators5;
|
|
@ -1,2 +0,0 @@
|
|||||||
mod strings1;
|
|
||||||
mod strings2;
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue