提交 8e2df470 编写于 作者: lean2's avatar lean2

第一周

上级 7ac53df6
# Exercise to Book Chapter mapping
| Exercise | Book Chapter |
|------------------------|--------------|
| variables | §3.1 |
| functions | §3.3 |
| if | §3.5 |
| move_semantics | §4.1 |
| primitive_types | §4.3 |
| structs | §5.1 |
| enums | §6 |
| modules | §7.2 |
| collections | §8.1 |
| strings | §8.2 |
| error_handling | §9 |
| generics | §10 |
| option | §10.1 |
| traits | §10.2 |
| tests | §11.1 |
| standard_library_types | §13.2 |
| threads | §16.1 |
| macros | §19.6 |
| clippy | n/a |
| conversions | n/a |
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "clippy2"
version = "0.0.1"
[package]
name = "clippy2"
version = "0.0.1"
edition = "2018"
[[bin]]
name = "clippy2"
path = "clippy2.rs"
\ No newline at end of file
# Clippy
The Clippy tool is a collection of lints to analyze your code so you can catch common mistakes and improve your Rust code.
If you used the installation script for Rustlings, Clippy should be already installed.
If not you can install it manually via `rustup component add clippy`.
## Further information
- [GitHub Repository](https://github.com/rust-lang/rust-clippy).
// clippy1.rs
// The Clippy tool is a collection of lints to analyze your code
// so you can catch common mistakes and improve your Rust code.
//
// For these exercises the code will fail to compile when there are clippy warnings
// check clippy's suggestions from the output to solve the exercise.
// Execute `rustlings hint clippy1` for hints :)
fn main() {
let x = 1.2331f64;
let y = 1.2332f64;
if (y - x).abs() > f64::EPSILON {
println!("Success!");
}
}
// clippy2.rs
// Make me compile! Execute `rustlings hint clippy2` for hints :)
fn main() {
let mut res = 42;
let option = Some(12);
if let Some(x) = option {
res += x;
}
println!("{}", res);
}
{"rustc_fingerprint":16017588165566947107,"outputs":{"3376809586958156926":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/xuebling/.rustup/toolchains/nightly-aarch64-apple-darwin\ndebug_assertions\nfeature=\"cargo-clippy\"\npanic=\"unwind\"\nproc_macro\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"crypto\"\ntarget_feature=\"dpb\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fp\"\ntarget_feature=\"fp16\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sha2\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nunix\n","stderr":""},"5912810005807744388":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n","stderr":""},"17598535894874457435":{"success":true,"status":"","code":0,"stdout":"rustc 1.55.0-nightly (149f4836d 2021-06-17)\nbinary: rustc\ncommit-hash: 149f4836dd6d9e789a26dca16dc034588866894e\ncommit-date: 2021-06-17\nhost: aarch64-apple-darwin\nrelease: 1.55.0-nightly\nLLVM version: 12.0.1\n","stderr":""}},"successes":{}}
\ No newline at end of file
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/
{"rustc":3380350255009067448,"features":"[]","target":16665255304623381240,"profile":7235557421335335211,"path":6082996812075136117,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/clippy2-53a50a64aa65ffc3/dep-bin-clippy2"}}],"rustflags":[],"metadata":7797948686568424061,"config":0,"compile_kind":0}
\ No newline at end of file
This file has an mtime of when this was started.
\ No newline at end of file
/Users/xuebling/Project/System/rustlings/exercises/clippy/target/debug/deps/clippy2-53a50a64aa65ffc3.rmeta: clippy2.rs
/Users/xuebling/Project/System/rustlings/exercises/clippy/target/debug/deps/clippy2-53a50a64aa65ffc3.d: clippy2.rs
clippy2.rs:
# env-dep:CLIPPY_ARGS=-D__CLIPPY_HACKERY__warnings__CLIPPY_HACKERY__
# Collections
Rust’s standard library includes a number of very useful data
structures called collections. Most other data types represent one
specific value, but collections can contain multiple values. Unlike
the built-in array and tuple types, the data these collections point
to is stored on the heap, which means the amount of data does not need
to be known at compile time and can grow or shrink as the program
runs.
This exercise will get you familiar with two fundamental data
structures that are used very often in Rust programs:
* A *vector* allows you to store a variable number of values next to
each other.
* 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.
## Further information
- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html)
// hashmap1.rs
// 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 particular fruit is in the basket. You have to put
// at least three different types of fruits (e.g apple, banana, mango)
// in the basket and the total count of all the fruits should be at
// least five.
//
// Make me compile and pass the tests!
//
// Execute the command `rustlings hint hashmap1` if you need
// hints.
use std::collections::HashMap;
fn fruit_basket() -> HashMap<String, u32> {
let mut basket = HashMap::new();// TODO: declare your hash map here.
// Two bananas are already given for you :)
basket.insert(String::from("banana"), 2);
// TODO: Put more fruits in your basket here.
basket.insert(String::from("grape"), 2);
basket.insert(String::from("orange"), 2);
basket.insert(String::from("apple"), 2);
basket
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn at_least_three_types_of_fruits() {
let basket = fruit_basket();
assert!(basket.len() >= 3);
}
#[test]
fn at_least_five_fruits() {
let basket = fruit_basket();
assert!(basket.values().sum::<u32>() >= 5);
}
}
// hashmap2.rs
// A basket of fruits in the form of a hash map is given. The key
// represents the name of the fruit and the value represents how many
// of that particular fruit is in the basket. You have to put *MORE
// THAN 11* fruits in the basket. Three types of fruits - Apple (4),
// Mango (2) and Lychee (5) are already given in the basket. You are
// not allowed to insert any more of these fruits!
//
// Make me pass the tests!
//
// Execute the command `rustlings hint hashmap2` if you need
// hints.
use std::collections::HashMap;
#[derive(Hash, PartialEq, Eq)]
enum Fruit {
Apple,
Banana,
Mango,
Lychee,
Pineapple,
}
fn fruit_basket(basket: &mut HashMap<Fruit, u32>) {
let fruit_kinds = vec![
Fruit::Apple,
Fruit::Banana,
Fruit::Mango,
Fruit::Lychee,
Fruit::Pineapple,
];
for fruit in fruit_kinds {
// TODO: Put new fruits if not already present. Note that you
// are not allowed to put any type of fruit that's already
// present!
if !basket.contains_key(&fruit) {
basket.insert(fruit, 10);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn get_fruit_basket() -> HashMap<Fruit, u32> {
let mut basket = HashMap::<Fruit, u32>::new();
basket.insert(Fruit::Apple, 4);
basket.insert(Fruit::Mango, 2);
basket.insert(Fruit::Lychee, 5);
basket
}
#[test]
fn test_given_fruits_are_not_modified() {
let mut basket = get_fruit_basket();
fruit_basket(&mut basket);
assert_eq!(*basket.get(&Fruit::Apple).unwrap(), 4);
assert_eq!(*basket.get(&Fruit::Mango).unwrap(), 2);
assert_eq!(*basket.get(&Fruit::Lychee).unwrap(), 5);
}
#[test]
fn at_least_five_types_of_fruits() {
let mut basket = get_fruit_basket();
fruit_basket(&mut basket);
let count_fruit_kinds = basket.len();
assert!(count_fruit_kinds >= 5);
}
#[test]
fn greater_than_eleven_fruits() {
let mut basket = get_fruit_basket();
fruit_basket(&mut basket);
let count = basket.values().sum::<u32>();
assert!(count > 11);
}
}
// vec1.rs
// Your task is to create a `Vec` which holds the exact same elements
// as in the array `a`.
// Make me compile and pass the test!
// Execute the command `rustlings hint vec1` if you need hints.
fn array_and_vec() -> ([i32; 4], Vec<i32>) {
let a = [10, 20, 30, 40]; // a plain array
let v = vec![10,20,30,40];// TODO: declare your vector here with the macro for vectors
(a, v)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_array_and_vec_similarity() {
let (a, v) = array_and_vec();
assert_eq!(a, v[..]);
}
}
// vec2.rs
// A Vec of even numbers is given. Your task is to complete the loop
// so that each number in the Vec is multiplied by 2.
//
// Make me pass the test!
//
// Execute the command `rustlings hint vec2` if you need
// hints.
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
for i in v.iter_mut() {
// TODO: Fill this up so that each element in the Vec `v` is
// multiplied by 2.
*i *= 2;
}
// At this point, `v` should be equal to [4, 8, 12, 16, 20].
v
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vec_loop() {
let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
let ans = vec_loop(v.clone());
assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
}
}
# Type conversions
Rust offers a multitude of ways to convert a value of a given type into another type.
The simplest form of type conversion is a type cast expression. It is denoted with the binary operator `as`. For instance, `println!("{}", 1 + 1.0);` would not compile, since `1` is an integer while `1.0` is a float. However, `println!("{}", 1 as f32 + 1.0)` should compile. The exercise [`using_as`](using_as.rs) tries to cover this.
Rust also offers traits that facilitate type conversions upon implementation. These traits can be found under the [`convert`](https://doc.rust-lang.org/std/convert/index.html) module.
The traits are the following:
- `From` and `Into` covered in [`from_into`](from_into.rs)
- `TryFrom` and `TryInto` covered in [`try_from_into`](try_from_into.rs)
- `AsRef` and `AsMut` covered in [`as_ref_mut`](as_ref_mut.rs)
Furthermore, the `std::str` module offers a trait called [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) which helps with converting strings into target types via the `parse` method on strings. If properly implemented for a given type `Person`, then `let p: Person = "Mark,20".parse().unwrap()` should both compile and run without panicking.
These should be the main ways ***within the standard library*** to convert data into your desired types.
## Further information
These are not directly covered in the book, but the standard library has a great documentation for it.
- [conversions](https://doc.rust-lang.org/std/convert/index.html)
- [`FromStr` trait](https://doc.rust-lang.org/std/str/trait.FromStr.html)
\ No newline at end of file
// AsRef and AsMut allow for cheap reference-to-reference conversions.
// Read more about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html
// and https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively.
// Obtain the number of bytes (not characters) in the given argument
// Add the AsRef trait appropriately as a trait bound
fn byte_counter<T: AsRef<str>>(arg: T) -> usize {
arg.as_ref().as_bytes().len()
}
// Obtain the number of characters (not bytes) in the given argument
// Add the AsRef trait appropriately as a trait bound
fn char_counter<T: AsRef<str>>(arg: T) -> usize {
arg.as_ref().chars().count()
}
fn main() {
let s = "Café au lait";
println!("{}", char_counter(s));
println!("{}", byte_counter(s));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn different_counts() {
let s = "Café au lait";
assert_ne!(char_counter(s), byte_counter(s));
}
#[test]
fn same_counts() {
let s = "Cafe au lait";
assert_eq!(char_counter(s), byte_counter(s));
}
#[test]
fn different_counts_using_string() {
let s = String::from("Café au lait");
assert_ne!(char_counter(s.clone()), byte_counter(s));
}
#[test]
fn same_counts_using_string() {
let s = String::from("Cafe au lait");
assert_eq!(char_counter(s.clone()), byte_counter(s));
}
}
// The From trait is used for value-to-value conversions.
// If From is implemented correctly for a type, the Into trait should work conversely.
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.From.html
#[derive(Debug)]
struct Person {
name: String,
age: usize,
}
// We implement the Default trait to use it as a fallback
// when the provided string is not convertible into a Person object
impl Default for Person {
fn default() -> Person {
Person {
name: String::from("John"),
age: 30,
}
}
}
// Your task is to complete this implementation
// in order for the line `let p = Person::from("Mark,20")` to compile
// Please note that you'll need to parse the age component into a `usize`
// with something like `"4".parse::<usize>()`. The outcome of this needs to
// be handled appropriately.
//
// Steps:
// 1. If the length of the provided string is 0, then return the default of Person
// 2. Split the given string on the commas present in it
// 3. Extract the first element from the split operation and use it as the name
// 4. If the name is empty, then return the default of Person
// 5. Extract the other element from the split operation and parse it into a `usize` as the age
// If while parsing the age, something goes wrong, then return the default of Person
// Otherwise, then return an instantiated Person object with the results
impl From<&str> for Person {
fn from(s: &str) -> Person {
let mut person = Person::default();
if s.is_empty() {
return person;
}
let mut res = s.split(",");
if let Some(name) = res.next() {
if name.is_empty() {
return Person::default();
}
person.name = name.to_string();
}else{
return Person::default();
}
if let Some(age) = res.next() {
let result = age.parse::<usize>();
if result.is_ok() {
person.age = result.unwrap();
}else{
return Person::default();
}
}else{
return Person::default();
}
if let Some(t) = res.next() {
return Person::default();
}
person
}
}
fn main() {
// Use the `from` function
let p1 = Person::from("Mark,20");
// Since From is implemented for Person, we should be able to use Into
let p2: Person = "Gerald,70".into();
println!("{:?}", p1);
println!("{:?}", p2);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default() {
// Test that the default person is 30 year old John
let dp = Person::default();
assert_eq!(dp.name, "John");
assert_eq!(dp.age, 30);
}
#[test]
fn test_bad_convert() {
// Test that John is returned when bad string is provided
let p = Person::from("");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_good_convert() {
// Test that "Mark,20" works
let p = Person::from("Mark,20");
assert_eq!(p.name, "Mark");
assert_eq!(p.age, 20);
}
#[test]
fn test_bad_age() {
// Test that "Mark,twenty" will return the default person due to an error in parsing age
let p = Person::from("Mark,twenty");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_missing_comma_and_age() {
let p: Person = Person::from("Mark");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_missing_age() {
let p: Person = Person::from("Mark,");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_missing_name() {
let p: Person = Person::from(",1");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_missing_name_and_age() {
let p: Person = Person::from(",");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_missing_name_and_invalid_age() {
let p: Person = Person::from(",one");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_trailing_comma() {
let p: Person = Person::from("Mike,32,");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
#[test]
fn test_trailing_comma_and_some_string() {
let p: Person = Person::from("Mike,32,man");
assert_eq!(p.name, "John");
assert_eq!(p.age, 30);
}
}
// This does practically the same thing that TryFrom<&str> does.
// Additionally, upon implementing FromStr, you can use the `parse` method
// on strings to generate an object of the implementor type.
// You can read more about it at https://doc.rust-lang.org/std/str/trait.FromStr.html
use std::error;
use std::str::FromStr;
#[derive(Debug)]
struct Person {
name: String,
age: usize,
}
// Steps:
// 1. If the length of the provided string is 0, an error should be returned
// 2. Split the given string on the commas present in it
// 3. Only 2 elements should be returned from the split, otherwise return an error
// 4. Extract the first element from the split operation and use it as the name
// 5. Extract the other element from the split operation and parse it into a `usize` as the age
// with something like `"4".parse::<usize>()`
// 5. If while extracting the name and the age something goes wrong, an error should be returned
// If everything goes well, then return a Result of a Person object
impl FromStr for Person {
type Err = String;
fn from_str(s: &str) -> Result<Person, Self::Err> {
if !s.contains(",") || s.len() == 0{
return Err(String::from("wrong"));
}
let s =s.split(",").collect::<Vec<_>>();
if s.len() != 2{
return Err(String::from("wrong"));
}
if s[0] ==""{
return Err(String::from("wrong"));
}
if let Ok(age) = s[1].parse::<usize>(){
return Ok(Person{
name: String::from(s[0]),
age: age
})
}
else{
return Err(String::from("age must be number"));
}
}
}
fn main() {
let p = "Mark,20".parse::<Person>().unwrap();
println!("{:?}", p);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_input() {
assert!("".parse::<Person>().is_err());
}
#[test]
fn good_input() {
let p = "John,32".parse::<Person>();
assert!(p.is_ok());
let p = p.unwrap();
assert_eq!(p.name, "John");
assert_eq!(p.age, 32);
}
#[test]
fn missing_age() {
assert!("John,".parse::<Person>().is_err());
}
#[test]
fn invalid_age() {
assert!("John,twenty".parse::<Person>().is_err());
}
#[test]
fn missing_comma_and_age() {
assert!("John".parse::<Person>().is_err());
}
#[test]
fn missing_name() {
assert!(",1".parse::<Person>().is_err());
}
#[test]
fn missing_name_and_age() {
assert!(",".parse::<Person>().is_err());
}
#[test]
fn missing_name_and_invalid_age() {
assert!(",one".parse::<Person>().is_err());
}
#[test]
fn trailing_comma() {
assert!("John,32,".parse::<Person>().is_err());
}
#[test]
fn trailing_comma_and_some_string() {
assert!("John,32,man".parse::<Person>().is_err());
}
}
// TryFrom is a simple and safe type conversion that may fail in a controlled way under some circumstances.
// Basically, this is the same as From. The main difference is that this should return a Result type
// instead of the target type itself.
// You can read more about it at https://doc.rust-lang.org/std/convert/trait.TryFrom.html
use std::convert::{TryFrom, TryInto};
use std::error;
use error::Error;
use std::fmt;
#[derive(Debug, PartialEq)]
struct Color {
red: u8,
green: u8,
blue: u8,
}
// Your task is to complete this implementation
// and return an Ok result of inner type Color.
// You need to create an implementation for a tuple of three integers,
// an array of three integers and a slice of integers.
//
// Note that the implementation for tuple and array will be checked at compile time,
// but the slice implementation needs to check the slice length!
// Also note that correct RGB color values must be integers in the 0..=255 range.
// Tuple implementation
#[derive(Debug)]
pub struct ColorError(String);
impl fmt::Display for ColorError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "There is an error: {}", self.0)
}
}
impl Error for ColorError {
}
fn check(num: i16) -> Result<u8, ColorError>{
if num <= 255 && num >= 0 {
Ok(num as u8)
}else {
Err(ColorError("wrong parameter".to_string()))
}
}
impl TryFrom<(i16, i16, i16)> for Color {
type Error = Box<dyn error::Error>;
fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
let red = check(tuple.0)?;
let green = check(tuple.1)?;
let blue = check(tuple.2)?;
Ok(Color{red,green,blue})
}
}
// Array implementation
impl TryFrom<[i16; 3]> for Color {
type Error = Box<dyn error::Error>;
fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {
let red = check(arr[0])?;
let green = check(arr[1])?;
let blue = check(arr[2])?;
Ok(Color{red,green,blue})
}
}
// Slice implementation
impl TryFrom<&[i16]> for Color {
type Error = Box<dyn error::Error>;
fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {
if slice.len() != 3 {
Err(Box::new(ColorError("wrong parameter".to_string())))
}else{
let red = check(slice[0])?;
let green = check(slice[1])?;
let blue = check(slice[2])?;
Ok(Color{red,green,blue})
}
}
}
fn main() {
// Use the `from` function
let c1 = Color::try_from((183, 65, 14));
println!("{:?}", c1);
// Since From is implemented for Color, we should be able to use Into
let c2: Result<Color, _> = [183, 65, 14].try_into();
println!("{:?}", c2);
let v = vec![183, 65, 14];
// With slice we should use `from` function
let c3 = Color::try_from(&v[..]);
println!("{:?}", c3);
// or take slice within round brackets and use Into
let c4: Result<Color, _> = (&v[..]).try_into();
println!("{:?}", c4);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tuple_out_of_range_positive() {
assert!(Color::try_from((256, 1000, 10000)).is_err());
}
#[test]
fn test_tuple_out_of_range_negative() {
assert!(Color::try_from((-1, -10, -256)).is_err());
}
#[test]
fn test_tuple_sum() {
assert!(Color::try_from((-1, 255, 255)).is_err());
}
#[test]
fn test_tuple_correct() {
let c: Result<Color, _> = (183, 65, 14).try_into();
assert!(c.is_ok());
assert_eq!(
c.unwrap(),
Color {
red: 183,
green: 65,
blue: 14
}
);
}
#[test]
fn test_array_out_of_range_positive() {
let c: Result<Color, _> = [1000, 10000, 256].try_into();
assert!(c.is_err());
}
#[test]
fn test_array_out_of_range_negative() {
let c: Result<Color, _> = [-10, -256, -1].try_into();
assert!(c.is_err());
}
#[test]
fn test_array_sum() {
let c: Result<Color, _> = [-1, 255, 255].try_into();
assert!(c.is_err());
}
#[test]
fn test_array_correct() {
let c: Result<Color, _> = [183, 65, 14].try_into();
assert!(c.is_ok());
assert_eq!(
c.unwrap(),
Color {
red: 183,
green: 65,
blue: 14
}
);
}
#[test]
fn test_slice_out_of_range_positive() {
let arr = [10000, 256, 1000];
assert!(Color::try_from(&arr[..]).is_err());
}
#[test]
fn test_slice_out_of_range_negative() {
let arr = [-256, -1, -10];
assert!(Color::try_from(&arr[..]).is_err());
}
#[test]
fn test_slice_sum() {
let arr = [-1, 255, 255];
assert!(Color::try_from(&arr[..]).is_err());
}
#[test]
fn test_slice_correct() {
let v = vec![183, 65, 14];
let c: Result<Color, _> = Color::try_from(&v[..]);
assert!(c.is_ok());
assert_eq!(
c.unwrap(),
Color {
red: 183,
green: 65,
blue: 14
}
);
}
#[test]
fn test_slice_excess_length() {
let v = vec![0, 0, 0, 0];
assert!(Color::try_from(&v[..]).is_err());
}
#[test]
fn test_slice_insufficient_length() {
let v = vec![0, 0];
assert!(Color::try_from(&v[..]).is_err());
}
}
// Type casting in Rust is done via the usage of the `as` operator.
// Please note that the `as` operator is not only used when type casting.
// It also helps with renaming imports.
//
// The goal is to make sure that the division does not fail to compile
// and returns the proper type.
fn average(values: &[f64]) -> f64 {
let total = values.iter().fold(0.0, |a, b| a + b);
total / values.len() as f64
}
fn main() {
let values = [3.5, 0.3, 13.0, 11.7];
println!("{}", average(&values));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn returns_proper_type_and_value() {
assert_eq!(average(&[3.5, 0.3, 13.0, 11.7]), 7.125);
}
}
# Enums
Rust allows you to define types called "enums" which enumerate possible values.
Enums are a feature in many languages, but their capabilities differ in each language. Rust’s enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell.
Useful in combination with enums is Rust's "pattern matching" facility, which makes it easy to run different code for different values of an enumeration.
## Further information
- [Enums](https://doc.rust-lang.org/book/ch06-00-enums.html)
- [Pattern syntax](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html)
// enums1.rs
// Make me compile! Execute `rustlings hint enums1` for hints!
#[derive(Debug)]
enum Message {
// TODO: define a few types of messages as used below
Quit,
Echo,
Move,
ChangeColor
}
fn main() {
println!("{:?}", Message::Quit);
println!("{:?}", Message::Echo);
println!("{:?}", Message::Move);
println!("{:?}", Message::ChangeColor);
}
// enums2.rs
// Make me compile! Execute `rustlings hint enums2` for hints!
#[derive(Debug)]
enum Message {
// TODO: define the different variants used below
Move{
x: i32,
y: i32
},
Echo(String),
ChangeColor(i32,i32,i32),
Quit
}
impl Message {
fn call(&self) {
println!("{:?}", &self);
}
}
fn main() {
let messages = [
Message::Move { x: 10, y: 30 },
Message::Echo(String::from("hello world")),
Message::ChangeColor(200, 255, 255),
Message::Quit,
];
for message in &messages {
message.call();
}
}
// enums3.rs
// Address all the TODOs to make the tests pass!
enum Message {
// TODO: implement the message variant types based on their usage below
Move(Point),
Echo(String),
ChangeColor((u8,u8,u8)),
Quit
}
struct Point {
x: u8,
y: u8,
}
struct State {
color: (u8, u8, u8),
position: Point,
quit: bool,
}
impl State {
fn change_color(&mut self, color: (u8, u8, u8)) {
self.color = color;
}
fn quit(&mut self) {
self.quit = true;
}
fn echo(&self, s: String) {
println!("{}", s);
}
fn move_position(&mut self, p: Point) {
self.position = p;
}
fn process(&mut self, message: Message) {
// TODO: create a match expression to process the different message variants
match message {
Message::Move(point) => {
self.move_position(point);
},
Message::Echo(str) => {
self.echo(str);
},
Message::ChangeColor(color) => {
self.change_color(color);
},
Message::Quit => {
self.quit();
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_match_message_call() {
let mut state = State {
quit: false,
position: Point { x: 0, y: 0 },
color: (0, 0, 0),
};
state.process(Message::ChangeColor((255, 0, 255)));
state.process(Message::Echo(String::from("hello world")));
state.process(Message::Move(Point { x: 10, y: 15 }));
state.process(Message::Quit);
assert_eq!(state.color, (255, 0, 255));
assert_eq!(state.position.x, 10);
assert_eq!(state.position.y, 15);
assert_eq!(state.quit, true);
}
}
# Error handling
Most errors aren’t serious enough to require the program to stop entirely.
Sometimes, when a function fails, it’s for a reason that you can easily interpret and respond to.
For example, if you try to open a file and that operation fails because the file doesn’t exist, you might want to create the file instead of terminating the process.
## Further information
- [Error Handling](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html)
- [Generics](https://doc.rust-lang.org/book/ch10-01-syntax.html)
- [Result](https://doc.rust-lang.org/rust-by-example/error/result.html)
- [Boxing errors](https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/boxing_errors.html)
// errors1.rs
// This function refuses to generate text to be printed on a nametag if
// you pass it an empty string. It'd be nicer if it explained what the problem
// was, instead of just sometimes returning `None`. The 2nd test currently
// does not compile or pass, but it illustrates the behavior we would like
// this function to have.
// Execute `rustlings hint errors1` for hints!
pub fn generate_nametag_text(name: String) -> Option<String> {
if name.len() > 0 {
Some(format!("Hi! My name is {}", name))
} else {
// Empty names aren't allowed.
None
}
}
#[cfg(test)]
mod tests {
use super::*;
// This test passes initially if you comment out the 2nd test.
// You'll need to update what this test expects when you change
// the function under test!
#[test]
fn generates_nametag_text_for_a_nonempty_name() {
assert_eq!(
generate_nametag_text("Beyoncé".into()),
Some("Hi! My name is Beyoncé".into())
);
}
// #[test]
// fn explains_why_generating_nametag_text_fails() {
// assert_eq!(
// generate_nametag_text("".into()),
// Err("`name` was empty; it must be nonempty.".into())
// );
// }
}
// errors2.rs
// Say we're writing a game where you can buy items with tokens. All items cost
// 5 tokens, and whenever you purchase items there is a processing fee of 1
// token. A player of the game will type in how many items they want to buy,
// and the `total_cost` function will calculate the total number of tokens.
// Since the player typed in the quantity, though, we get it as a string-- and
// they might have typed anything, not just numbers!
// Right now, this function isn't handling the error case at all (and isn't
// handling the success case properly either). What we want to do is:
// if we call the `parse` function on a string that is not a number, that
// function will return a `ParseIntError`, and in that case, we want to
// immediately return that error from our function and not try to multiply
// and add.
// There are at least two ways to implement this that are both correct-- but
// one is a lot shorter! Execute `rustlings hint errors2` for hints to both ways.
use std::num::ParseIntError;
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>();
if qty.is_err() {
Err(qty.unwrap_err())
}else {
Ok(qty.unwrap() * cost_per_item + processing_fee)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn item_quantity_is_a_valid_number() {
assert_eq!(total_cost("34"), Ok(171));
}
#[test]
fn item_quantity_is_an_invalid_number() {
assert_eq!(
total_cost("beep boop").unwrap_err().to_string(),
"invalid digit found in string"
);
}
}
// errors3.rs
// This is a program that is trying to use a completed version of the
// `total_cost` function from the previous exercise. It's not working though!
// Why not? What should we do to fix it?
// Execute `rustlings hint errors3` for hints!
use std::num::ParseIntError;
fn main() {
let mut tokens = 100;
let pretend_user_input = "8";
let cost = total_cost(pretend_user_input).unwrap();
if cost > tokens {
println!("You can't afford that many!");
} else {
tokens -= cost;
println!("You now have {} tokens.", tokens);
}
}
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
let processing_fee = 1;
let cost_per_item = 5;
let qty = item_quantity.parse::<i32>()?;
Ok(qty * cost_per_item + processing_fee)
}
// errorsn.rs
// This is a bigger error exercise than the previous ones!
// You can do it! :)
//
// Edit the `read_and_validate` function ONLY. Don't create any Errors
// that do not already exist.
//
// So many things could go wrong!
//
// - Reading from stdin could produce an io::Error
// - Parsing the input could produce a num::ParseIntError
// - Validating the input could produce a CreationError (defined below)
//
// How can we lump these errors into one general error? That is, what
// type goes where the question marks are, and how do we return
// that type from the body of read_and_validate?
//
// Execute `rustlings hint errorsn` for hints :)
use std::error;
use std::fmt;
use std::io;
// PositiveNonzeroInteger is a struct defined below the tests.
fn read_and_validate(b: &mut dyn io::BufRead) -> Result<PositiveNonzeroInteger, Box<dyn error::Error + 'static>> {
let mut line = String::new();
b.read_line(&mut line)?;
let num: i64 = line.trim().parse()?;
let answer = PositiveNonzeroInteger::new(num)?;
Ok(answer)
}
//
// Nothing below this needs to be modified
//
// This is a test helper function that turns a &str into a BufReader.
fn test_with_str(s: &str) -> Result<PositiveNonzeroInteger, Box<dyn error::Error>> {
let mut b = io::BufReader::new(s.as_bytes());
read_and_validate(&mut b)
}
#[test]
fn test_success() {
let x = test_with_str("42\n");
assert_eq!(PositiveNonzeroInteger(42), x.unwrap());
}
#[test]
fn test_not_num() {
let x = test_with_str("eleven billion\n");
assert!(x.is_err());
}
#[test]
fn test_non_positive() {
let x = test_with_str("-40\n");
assert!(x.is_err());
}
#[test]
fn test_ioerror() {
struct Broken;
impl io::Read for Broken {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(io::Error::new(io::ErrorKind::BrokenPipe, "uh-oh!"))
}
}
let mut b = io::BufReader::new(Broken);
assert!(read_and_validate(&mut b).is_err());
assert_eq!("uh-oh!", read_and_validate(&mut b).unwrap_err().to_string());
}
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
if value == 0 {
Err(CreationError::Zero)
} else if value < 0 {
Err(CreationError::Negative)
} else {
Ok(PositiveNonzeroInteger(value as u64))
}
}
}
#[test]
fn test_positive_nonzero_integer_creation() {
assert!(PositiveNonzeroInteger::new(10).is_ok());
assert_eq!(
Err(CreationError::Negative),
PositiveNonzeroInteger::new(-10)
);
assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
}
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}
impl fmt::Display for CreationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let description = match *self {
CreationError::Negative => "Number is negative",
CreationError::Zero => "Number is zero",
};
f.write_str(description)
}
}
impl error::Error for CreationError {}
// result1.rs
// Make this test pass! Execute `rustlings hint result1` for hints :)
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
#[derive(PartialEq, Debug)]
enum CreationError {
Negative,
Zero,
}
impl PositiveNonzeroInteger {
fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
if value < 0 {
return Err(CreationError::Negative);
}
if value == 0 {
return Err(CreationError::Zero);
}
Ok(PositiveNonzeroInteger(value as u64))
}
}
#[test]
fn test_creation() {
assert!(PositiveNonzeroInteger::new(10).is_ok());
assert_eq!(
Err(CreationError::Negative),
PositiveNonzeroInteger::new(-10)
);
assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0));
}
# Functions
Here, you'll learn how to write functions and how Rust's compiler can trace things way back.
## Further information
- [How Functions Work](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html)
// functions1.rs
// Make me compile! Execute `rustlings hint functions1` for hints :)
fn call_me(){
println!("")
}
fn main() {
call_me();
}
// functions2.rs
// Make me compile! Execute `rustlings hint functions2` for hints :)
fn main() {
call_me(3);
}
fn call_me(num:i32) {
for i in 0..num {
println!("Ring! Call number {}", i + 1);
}
}
// functions3.rs
// Make me compile! Execute `rustlings hint functions3` for hints :)
fn main() {
call_me(3);
}
fn call_me(num: u32) {
for i in 0..num {
println!("Ring! Call number {}", i + 1);
}
}
// functions4.rs
// Make me compile! Execute `rustlings hint functions4` for hints :)
// This store is having a sale where if the price is an even number, you get
// 10 Rustbucks off, but if it's an odd number, it's 3 Rustbucks off.
fn main() {
let original_price = 51;
println!("Your sale price is {}", sale_price(original_price));
}
fn sale_price(price: i32) -> i32{
if is_even(price) {
price - 10
} else {
price - 3
}
}
fn is_even(num: i32) -> bool {
num % 2 == 0
}
// functions5.rs
// Make me compile! Execute `rustlings hint functions5` for hints :)
fn main() {
let answer = square(3);
println!("The answer is {}", answer);
}
fn square(num: i32) -> i32 {
num * num
}
# Generics
Generics is the topic of generalizing types and functionalities to broader cases.
This is extremely useful for reducing code duplication in many ways, but can call for rather involving syntax.
Namely, being generic requires taking great care to specify over which types a generic type is actually considered valid.
The simplest and most common use of generics is for type parameters.
## Further information
- [Generic Data Types](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html)
- [Bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html)
// This shopping list program isn't compiling!
// Use your knowledge of generics to fix it.
fn main() {
let mut shopping_list: Vec<&str> = Vec::new();
shopping_list.push("milk");
}
// This powerful wrapper provides the ability to store a positive integer value.
// Rewrite it using generics so that it supports wrapping ANY type.
struct Wrapper<T> {
value: T,
}
impl <T> Wrapper<T> {
pub fn new(value: T) -> Self {
Wrapper { value }
}
}
#[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");
}
}
// 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!
use std::fmt::Display;
pub struct ReportCard<T: Display> {
pub grade: T,
pub student_name: String,
pub student_age: u8,
}
impl <T:Display> ReportCard<T> {
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: "A+",
student_name: "Gary Plotter".to_string(),
student_age: 11,
};
assert_eq!(
report_card.print(),
"Gary Plotter (11) - achieved a grade of A+"
);
}
}
# If
`if`, the most basic type of control flow, is what you'll learn here.
## Further information
- [Control Flow - if expressions](https://doc.rust-lang.org/book/ch03-05-control-flow.html#if-expressions)
// if1.rs
pub fn bigger(a: i32, b: i32) -> i32 {
// Complete this function to return the bigger number!
// Do not use:
// - another function call
// - additional variables
// Execute `rustlings hint if1` for hints
if a > b {
a
}else{
b
}
}
// Don't mind this for now :)
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ten_is_bigger_than_eight() {
assert_eq!(10, bigger(10, 8));
}
#[test]
fn fortytwo_is_bigger_than_thirtytwo() {
assert_eq!(42, bigger(32, 42));
}
}
// if2.rs
// Step 1: Make me compile!
// Step 2: Get the bar_for_fuzz and default_to_baz tests passing!
// Execute the command `rustlings hint if2` if you want a hint :)
pub fn fizz_if_foo(fizzish: &str) -> &str {
if fizzish == "fizz" {
"foo"
} else if fizzish == "fuzz" {
"bar"
} else {
"baz"
}
}
// No test changes needed!
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn foo_for_fizz() {
assert_eq!(fizz_if_foo("fizz"), "foo")
}
#[test]
fn bar_for_fuzz() {
assert_eq!(fizz_if_foo("fuzz"), "bar")
}
#[test]
fn default_to_baz() {
assert_eq!(fizz_if_foo("literally anything"), "baz")
}
}
# Macros
Rust's macro system is very powerful, but also kind of difficult to wrap your
head around. We're not going to teach you how to write your own fully-featured
macros. Instead, we'll show you how to use and create them.
## Further information
- [Macros](https://doc.rust-lang.org/book/ch19-06-macros.html)
- [The Little Book of Rust Macros](https://danielkeep.github.io/tlborm/book/index.html)
// macros1.rs
// Make me compile! Execute `rustlings hint macros1` for hints :)
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
fn main() {
my_macro!();
}
// macros2.rs
// Make me compile! Execute `rustlings hint macros2` for hints :)
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
fn main() {
my_macro!();
}
// macros3.rs
// Make me compile, without taking the macro out of the module!
// Execute `rustlings hint macros3` for hints :)
#[macro_use]
mod macros {
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
}
fn main() {
my_macro!();
}
// macros4.rs
// Make me compile! Execute `rustlings hint macros4` for hints :)
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
($val:expr) => {
println!("Look at this other macro: {}", $val);
};
}
fn main() {
my_macro!();
my_macro!(7777);
}
# Modules
In this section we'll give you an introduction to Rust's module system.
## Further information
- [The Module System](https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html)
// modules1.rs
// Make me compile! Execute `rustlings hint modules1` for hints :)
mod sausage_factory {
pub fn make_sausage() {
println!("sausage!");
}
}
fn main() {
sausage_factory::make_sausage();
}
// modules2.rs
// Make me compile! Execute `rustlings hint modules2` for hints :)
mod delicious_snacks {
pub use self::fruits::PEAR as fruit;
pub use self::veggies::CUCUMBER as veggie;
mod fruits {
pub const PEAR: &'static str = "Pear";
pub const APPLE: &'static str = "Apple";
}
mod veggies {
pub const CUCUMBER: &'static str = "Cucumber";
pub const CARROT: &'static str = "Carrot";
}
}
fn main() {
println!(
"favorite snacks: {} and {}",
delicious_snacks::fruit,
delicious_snacks::veggie
);
}
# Move Semantics
These exercises are adapted from [pnkfelix](https://github.com/pnkfelix)'s [Rust Tutorial](https://pnkfelix.github.io/rust-examples-icfp2014/) -- Thank you Felix!!!
## Further information
For this section, the book links are especially important.
- [Ownership](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html)
- [Reference and borrowing](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html)
// move_semantics1.rs
// Make me compile! Execute `rustlings hint move_semantics1` for hints :)
fn main() {
let vec0 = Vec::new();
let mut vec1 = fill_vec(vec0);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
vec1.push(88);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
}
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let mut vec = vec;
vec.push(22);
vec.push(44);
vec.push(66);
vec
}
// move_semantics2.rs
// Make me compile without changing line 13!
// Execute `rustlings hint move_semantics2` for hints :)
fn main() {
let vec0 = Vec::new();
let mut vec1 = fill_vec(vec0.clone());
// Do not change the following line!
println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0);
vec1.push(88);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
}
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let mut vec = vec;
vec.push(22);
vec.push(44);
vec.push(66);
vec
}
// move_semantics3.rs
// Make me compile without adding new lines-- just changing existing lines!
// (no lines with multiple semicolons necessary!)
// Execute `rustlings hint move_semantics3` for hints :)
fn main() {
let vec0= Vec::new();
let mut vec1 = fill_vec(vec0);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
vec1.push(88);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
}
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let mut vec = vec;
vec.push(22);
vec.push(44);
vec.push(66);
vec
}
// move_semantics4.rs
// Refactor this code so that instead of having `vec0` and creating the vector
// in `fn main`, we create it within `fn fill_vec` and transfer the
// freshly created vector from fill_vec to its caller.
// Execute `rustlings hint move_semantics4` for hints!
fn main() {
let mut vec1 = fill_vec();
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
vec1.push(88);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
}
// `fill_vec()` no longer takes `vec: Vec<i32>` as argument
fn fill_vec() -> Vec<i32> {
let mut vec = Vec::new();
vec.push(22);
vec.push(44);
vec.push(66);
vec
}
# Option
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:
- Initial values
- Return values for functions that are not defined over their entire input range (partial functions)
- Return value for otherwise reporting simple errors, where None is returned on error
- Optional struct fields
- Struct fields that can be loaned or "taken"
- Optional function arguments
- Nullable pointers
- Swapping things out of difficult situations
## Further Information
- [Option Enum Format](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-enum-definitions)
- [Option Module Documentation](https://doc.rust-lang.org/std/option/)
- [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html)
// option1.rs
// Make me compile! Execute `rustlings hint option1` for hints
// you can modify anything EXCEPT for this function's sig
fn print_number(maybe_number: Option<u16>) {
println!("printing: {}", maybe_number.unwrap());
}
fn main() {
print_number(Option::from(13));
print_number(Option::from(99));
let mut numbers: [Option<u16>; 5] = [Option::None;5];
for iter in 0..5 {
let number_to_add: u16 = {
((iter * 1235) + 2) / (4 * 16)
};
numbers[iter as usize] = Option::from(number_to_add);
}
}
// option2.rs
// Make me compile! Execute `rustlings hint option2` for hints
fn main() {
let optional_word = Some(String::from("rustlings"));
// TODO: Make this an if let statement whose value is "Some" type
if let Some(word) = optional_word {
println!("The word is: {}", word);
} else {
println!("The optional word doesn't contain anything");
}
let mut optional_integers_vec: Vec<Option<i8>> = Vec::new();
for x in 1..10 {
optional_integers_vec.push(Some(x));
}
// TODO: make this a while let statement - remember that vector.pop also adds another layer of Option<T>
// You can stack `Option<T>`'s into while let and if let
if let Some(integer) = optional_integers_vec.pop() {
println!("current value: {}", integer.unwrap());
}
}
# Primitive Types
Rust has a couple of basic types that are directly implemented into the
compiler. In this section, we'll go through the most important ones.
## Further information
- [Data Types](https://doc.rust-lang.org/stable/book/ch03-02-data-types.html)
- [The Slice Type](https://doc.rust-lang.org/stable/book/ch04-03-slices.html)
// primitive_types1.rs
// Fill in the rest of the line that has code missing!
// No hints, there's no tricks, just get used to typing these :)
fn main() {
// Booleans (`bool`)
let is_morning = true;
if is_morning {
println!("Good morning!");
}
let is_evening = true;// Finish the rest of this line like the example! Or make it be false!
if is_evening {
println!("Good evening!");
}
}
// primitive_types2.rs
// Fill in the rest of the line that has code missing!
// No hints, there's no tricks, just get used to typing these :)
fn main() {
// Characters (`char`)
let my_first_initial = 'C';
if my_first_initial.is_alphabetic() {
println!("Alphabetical!");
} else if my_first_initial.is_numeric() {
println!("Numerical!");
} else {
println!("Neither alphabetic nor numeric!");
}
let your_character = 'a';// Finish this line like the example! What's your favorite character?
// Try a letter, try a number, try a special character, try a character
// from a different language than your own, try an emoji!
if your_character.is_alphabetic() {
println!("Alphabetical!");
} else if your_character.is_numeric() {
println!("Numerical!");
} else {
println!("Neither alphabetic nor numeric!");
}
}
// primitive_types3.rs
// Create an array with at least 100 elements in it where the ??? is.
// Execute `rustlings hint primitive_types3` for hints!
fn main() {
let a = [1;100];
if a.len() >= 100 {
println!("Wow, that's a big array!");
} else {
println!("Meh, I eat arrays like that for breakfast.");
}
}
// primitive_types4.rs
// Get a slice out of Array a where the ??? is so that the test passes.
// Execute `rustlings hint primitive_types4` for hints!!
#[test]
fn slice_out_of_array() {
let a = [1, 2, 3, 4, 5];
let nice_slice = &a[1..4];
assert_eq!([2, 3, 4], nice_slice)
}
// primitive_types5.rs
// Destructure the `cat` tuple so that the println will work.
// Execute `rustlings hint primitive_types5` for hints!
fn main() {
let cat = ("Furry McFurson", 3.5);
let (name, age)/* your pattern here */ = cat;
println!("{} is {} years old.", name, age);
}
// primitive_types6.rs
// Use a tuple index to access the second element of `numbers`.
// You can put the expression for the second element where ??? is so that the test passes.
// Execute `rustlings hint primitive_types6` for hints!
#[test]
fn indexing_tuple() {
let numbers = (1, 2, 3);
// Replace below ??? with the tuple indexing syntax.
let second = numbers.1;
assert_eq!(2, second,
"This is not the 2nd number in the tuple!")
}
// quiz1.rs
// This is a quiz for the following sections:
// - Variables
// - Functions
// Mary is buying apples. One apple usually costs 2 Rustbucks, but if you buy
// more than 40 at once, each apple only costs 1! Write a function that calculates
// the price of an order of apples given the order amount. No hints this time!
// Put your function here!
// fn ..... {
fn calculate_apple_price(num: i32) -> i32 {
if num > 40{
num
}else {
num * 2
}
}
// Don't modify this function!
#[test]
fn verify_test() {
let price1 = calculate_apple_price(35);
let price2 = calculate_apple_price(65);
assert_eq!(70, price1);
assert_eq!(65, price2);
}
// quiz2.rs
// This is a quiz for the following sections:
// - Strings
// Ok, here are a bunch of values-- some are `String`s, some are `&str`s. Your
// task is to call one of these two functions on each value depending on what
// you think each value is. That is, add either `string_slice` or `string`
// before the parentheses on each line. If you're right, it will compile!
fn string_slice(arg: &str) {
println!("{}", arg);
}
fn string(arg: String) {
println!("{}", arg);
}
fn main() {
string_slice("blue");
string("red".to_string());
string(String::from("hi"));
string("rust is fun!".to_owned());
string("nice weather".into());
string(format!("Interpolation {}", "Station"));
string_slice(&String::from("abc")[0..1]);
string_slice(" hello there ".trim());
string("Happy Monday!".to_string().replace("Mon", "Tues"));
string("mY sHiFt KeY iS sTiCkY".to_lowercase());
}
// quiz3.rs
// This is a quiz for the following sections:
// - Tests
// This quiz isn't testing our function -- make it do that in such a way that
// the test passes. Then write a second test that tests that we get the result
// we expect to get when we call `times_two` with a negative number.
// No hints, you can do this :)
pub fn times_two(num: i32) -> i32 {
num * 2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn returns_twice_of_positive_numbers() {
assert_eq!(times_two(4), 8);
}
#[test]
fn returns_twice_of_negative_numbers() {
// TODO replace unimplemented!() with an assert for `times_two(-4)`
assert_eq!(times_two(-4),-8)
}
}
// quiz4.rs
// This quiz covers the sections:
// - Modules
// - Macros
// Write a macro that passes the quiz! No hints this time, you can do it!
macro_rules! my_macro {
($val:expr) => {
"Hello ".to_string() + $val
};
}
#[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!");
}
}
# Standard library types
This section will teach you about Box, Shared-State Concurrency and Iterators.
## Further information
- [Using Box to Point to Data on the Heap](https://doc.rust-lang.org/book/ch15-01-box.html)
- [Shared-State Concurrency](https://doc.rust-lang.org/book/ch16-03-shared-state.html)
- [Iterator](https://doc.rust-lang.org/book/ch13-02-iterators.html)
- [Iterator documentation](https://doc.rust-lang.org/stable/std/iter/)
// arc1.rs
// In this exercise, we are given a Vec of u32 called "numbers" with values ranging
// from 0 to 99 -- [ 0, 1, 2, ..., 98, 99 ]
// We would like to use this set of numbers within 8 different threads simultaneously.
// Each thread is going to get the sum of every eighth value, with an offset.
// The first thread (offset 0), will sum 0, 8, 16, ...
// The second thread (offset 1), will sum 1, 9, 17, ...
// The third thread (offset 2), will sum 2, 10, 18, ...
// ...
// The eighth thread (offset 7), will sum 7, 15, 23, ...
// Because we are using threads, our values need to be thread-safe. Therefore,
// we are using Arc. We need to make a change in each of the two TODOs.
// Make this code compile by filling in a value for `shared_numbers` where the
// first TODO comment is, and create an initial binding for `child_numbers`
// where the second TODO comment is. Try not to create any copies of the `numbers` Vec!
// Execute `rustlings hint arc1` for hints :)
#![forbid(unused_imports)] // Do not change this, (or the next) line.
use std::sync::Arc;
use std::thread;
fn main() {
let numbers: Vec<_> = (0..100u32).collect();
let shared_numbers = Arc::new(numbers);// TODO
let mut joinhandles = Vec::new();
for offset in 0..8 {
let child_numbers = shared_numbers.clone();// TODO
joinhandles.push(thread::spawn(move || {
let mut i = offset;
let mut sum = 0;
while i < child_numbers.len() {
sum += child_numbers[i];
i += 8;
}
println!("Sum of offset {} is {}", offset, sum);
}));
}
for handle in joinhandles.into_iter() {
handle.join().unwrap();
}
}
// box1.rs
//
// At compile time, Rust needs to know how much space a type takes up. This becomes problematic
// for recursive types, where a value can have as part of itself another value of the same type.
// To get around the issue, we can use a `Box` - a smart pointer used to store data on the heap,
// which also allows us to wrap a recursive type.
//
// The recursive type we're implementing in this exercise is the `cons list` - a data structure
// frequently found in functional programming languages. Each item in a cons list contains two
// elements: the value of the current item and the next item. The last item is a value called `Nil`.
//
// Step 1: use a `Box` in the enum definition to make the code compile
// Step 2: create both empty and non-empty cons lists by replacing `unimplemented!()`
//
// Note: the tests should not be changed
//
// Execute `rustlings hint box1` for hints :)
#[derive(PartialEq, Debug)]
pub enum List {
Cons(i32, Box<List>),
Nil,
}
fn main() {
println!("This is an empty cons list: {:?}", create_empty_list());
println!(
"This is a non-empty cons list: {:?}",
create_non_empty_list()
);
}
pub fn create_empty_list() -> List {
List::Nil
}
pub fn create_non_empty_list() -> List {
List::Cons(1,Box::new(List::Nil))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_empty_list() {
assert_eq!(List::Nil, create_empty_list())
}
#[test]
fn test_create_non_empty_list() {
assert_ne!(create_empty_list(), create_non_empty_list())
}
}
// iterators1.rs
//
// Make me compile by filling in the `???`s
//
// When performing operations on elements within a collection, iterators are essential.
// This module helps you get familiar with the structure of using an iterator and
// how to go through elements within an iterable collection.
//
// Execute `rustlings hint iterators1` for hints :D
fn main () {
let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"];
let mut my_iterable_fav_fruits = my_fav_fruits.iter(); // TODO: Step 1
assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
assert_eq!(my_iterable_fav_fruits.next(), Some(&"custard apple")); // TODO: Step 2
assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado"));
assert_eq!(my_iterable_fav_fruits.next(), Some(&"peach")); // TODO: Step 2.1
assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry"));
assert_eq!(my_iterable_fav_fruits.next(), None); // TODO: Step 3
}
// iterators2.rs
// In this exercise, you'll learn some of the unique advantages that iterators
// can offer. Follow the steps to complete the exercise.
// As always, there are hints if you execute `rustlings hint iterators2`!
// Step 1.
// Complete the `capitalize_first` function.
// "hello" -> "Hello"
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
match c.next() {
None => String::new(),
Some(first) => first.to_ascii_uppercase().to_string() + c.as_str(),
}
}
// Step 2.
// Apply the `capitalize_first` function to a slice of string slices.
// Return a vector of strings.
// ["hello", "world"] -> ["Hello", "World"]
pub fn capitalize_words_vector(words: &[&str]) -> Vec<String> {
words.iter().map(|s|{capitalize_first(s)}).collect()
}
// Step 3.
// Apply the `capitalize_first` function again to a slice of string slices.
// Return a single string.
// ["hello", " ", "world"] -> "Hello World"
pub fn capitalize_words_string(words: &[&str]) -> String {
words.iter().map(|str| {capitalize_first(str)}).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
assert_eq!(capitalize_first("hello"), "Hello");
}
#[test]
fn test_empty() {
assert_eq!(capitalize_first(""), "");
}
#[test]
fn test_iterate_string_vec() {
let words = vec!["hello", "world"];
assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]);
}
#[test]
fn test_iterate_into_string() {
let words = vec!["hello", " ", "world"];
assert_eq!(capitalize_words_string(&words), "Hello World");
}
}
// iterators3.rs
// This is a bigger exercise than most of the others! You can do it!
// Here is your mission, should you choose to accept it:
// 1. Complete the divide function to get the first four tests to pass.
// 2. Get the remaining tests to pass by completing the result_with_list and
// list_of_results functions.
// Execute `rustlings hint iterators3` to get some hints!
#[derive(Debug, PartialEq, Eq)]
pub enum DivisionError {
NotDivisible(NotDivisibleError),
DivideByZero,
}
#[derive(Debug, PartialEq, Eq)]
pub struct NotDivisibleError {
dividend: i32,
divisor: i32,
}
// Calculate `a` divided by `b` if `a` is evenly divisible by `b`.
// Otherwise, return a suitable error.
pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
if b==0 {
return Err(DivisionError::DivideByZero);
}
if a%b == 0 {
Ok(a/b)
}else {
Err(DivisionError::NotDivisible(NotDivisibleError{dividend: a, divisor: b}))
}
}
// Complete the function and return a value of the correct type so the test passes.
// Desired output: Ok([1, 11, 1426, 3])
fn result_with_list() -> Result<Vec<i32>,DivisionError> {
let numbers = vec![27, 297, 38502, 81];
let division_results = numbers.into_iter().map(|n| divide(n, 27).unwrap()).collect::<Vec<i32>>();
Ok(division_results)
}
// Complete the function and return a value of the correct type so the test passes.
// Desired output: [Ok(1), Ok(11), Ok(1426), Ok(3)]
fn list_of_results() -> Vec<Result<i32,DivisionError>> {
let numbers = vec![27, 297, 38502, 81];
numbers.into_iter().map(|n| divide(n, 27)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_success() {
assert_eq!(divide(81, 9), Ok(9));
}
#[test]
fn test_not_divisible() {
assert_eq!(
divide(81, 6),
Err(DivisionError::NotDivisible(NotDivisibleError {
dividend: 81,
divisor: 6
}))
);
}
#[test]
fn test_divide_by_0() {
assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));
}
#[test]
fn test_divide_0_by_something() {
assert_eq!(divide(0, 81), Ok(0));
}
#[test]
fn test_result_with_list() {
assert_eq!(format!("{:?}", result_with_list()), "Ok([1, 11, 1426, 3])");
}
#[test]
fn test_list_of_results() {
assert_eq!(
format!("{:?}", list_of_results()),
"[Ok(1), Ok(11), Ok(1426), Ok(3)]"
);
}
}
// iterators4.rs
pub fn factorial(num: u64) -> u64 {
// Complete this function to return the factorial of num
// Do not use:
// - return
// Try not to use:
// - imperative style loops (for, while)
// - additional variables
// For an extra challenge, don't use:
// - recursion
// Execute `rustlings hint iterators4` for hints.
let mut res = 1;
(1..num+1).for_each(|n|{res*=n});
res
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn factorial_of_1() {
assert_eq!(1, factorial(1));
}
#[test]
fn factorial_of_2() {
assert_eq!(2, factorial(2));
}
#[test]
fn factorial_of_4() {
assert_eq!(24, factorial(4));
}
}
// iterators5.rs
// Let's define a simple model to track Rustlings exercise progress. Progress
// will be modelled using a hash map. The name of the exercise is the key and
// the progress is the value. Two counting functions were created to count the
// number of exercises with a given progress. These counting functions use
// imperative style for loops. Recreate this counting functionality using
// iterators. Only the two iterator methods (count_iterator and
// count_collection_iterator) need to be modified.
// Execute `rustlings hint
// iterators5` for hints.
//
// Make the code compile and the tests pass.
use std::collections::HashMap;
#[derive(PartialEq, Eq)]
enum Progress {
None,
Some,
Complete,
}
fn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize {
let mut count = 0;
for val in map.values() {
if val == &value {
count += 1;
}
}
count
}
fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
// map is a hashmap with String keys and Progress values.
// map = { "variables1": Complete, "from_str": None, ... }
count_for(map, value)
}
fn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
let mut count = 0;
for map in collection {
for val in map.values() {
if val == &value {
count += 1;
}
}
}
count
}
fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {
// collection is a slice of hashmaps.
// collection = [{ "variables1": Complete, "from_str": None, ... },
// { "variables2": Complete, ... }, ... ]
count_collection_for(collection, value)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn count_complete() {
let map = get_map();
assert_eq!(3, count_iterator(&map, Progress::Complete));
}
#[test]
fn count_equals_for() {
let map = get_map();
assert_eq!(
count_for(&map, Progress::Complete),
count_iterator(&map, Progress::Complete)
);
}
#[test]
fn count_collection_complete() {
let collection = get_vec_map();
assert_eq!(
6,
count_collection_iterator(&collection, Progress::Complete)
);
}
#[test]
fn count_collection_equals_for() {
let collection = get_vec_map();
assert_eq!(
count_collection_for(&collection, Progress::Complete),
count_collection_iterator(&collection, Progress::Complete)
);
}
fn get_map() -> HashMap<String, Progress> {
use Progress::*;
let mut map = HashMap::new();
map.insert(String::from("variables1"), Complete);
map.insert(String::from("functions1"), Complete);
map.insert(String::from("hashmap1"), Complete);
map.insert(String::from("arc1"), Some);
map.insert(String::from("as_ref_mut"), None);
map.insert(String::from("from_str"), None);
map
}
fn get_vec_map() -> Vec<HashMap<String, Progress>> {
use Progress::*;
let map = get_map();
let mut other = HashMap::new();
other.insert(String::from("variables2"), Complete);
other.insert(String::from("functions2"), Complete);
other.insert(String::from("if1"), Complete);
other.insert(String::from("from_into"), None);
other.insert(String::from("try_from_into"), None);
vec![map, other]
}
}
# Strings
Rust has two string types, a string slice (`&str`) and an owned string (`String`).
We're not going to dictate when you should use which one, but we'll show you how
to identify and create them, as well as use them.
## Further information
- [Strings](https://doc.rust-lang.org/book/ch08-02-strings.html)
// strings1.rs
// Make me compile without changing the function signature!
// Execute `rustlings hint strings1` for hints ;)
fn main() {
let answer = current_favorite_color();
println!("My current favorite color is {}", answer);
}
fn current_favorite_color() -> String {
"blue".to_string()
}
// strings2.rs
// Make me compile without changing the function signature!
// Execute `rustlings hint strings2` for hints :)
fn main() {
let word = String::from("green"); // Try not changing this line :)
if is_a_color_word(&word) {
println!("That is a color word I know!");
} else {
println!("That is not a color word I know.");
}
}
fn is_a_color_word(attempt: &str) -> bool {
attempt == "green" || attempt == "blue" || attempt == "red"
}
# Structs
Rust has three struct types: a classic C struct, a tuple struct, and a unit struct.
## Further information
- [Structures](https://doc.rust-lang.org/book/ch05-01-defining-structs.html)
- [Method Syntax](https://doc.rust-lang.org/book/ch05-03-method-syntax.html)
// structs1.rs
// Address all the TODOs to make the tests pass!
struct ColorClassicStruct {
// TODO: Something goes here
name: String,
hex: String
}
struct ColorTupleStruct(/* TODO: Something goes here */pub String,pub String);
#[derive(Debug)]
struct UnitStruct;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn classic_c_structs() {
// TODO: Instantiate a classic c struct!
let green = ColorClassicStruct{name: String::from("green"),hex: String::from("#00FF00")};
assert_eq!(green.name, "green");
assert_eq!(green.hex, "#00FF00");
}
#[test]
fn tuple_structs() {
// TODO: Instantiate a tuple struct!
let green = ColorTupleStruct(String::from("green"),String::from("#00FF00"));
assert_eq!(green.0, "green");
assert_eq!(green.1, "#00FF00");
}
#[test]
fn unit_structs() {
// TODO: Instantiate a unit struct!
let unit_struct = UnitStruct{};
let message = format!("{:?}s are fun!", unit_struct);
assert_eq!(message, "UnitStructs are fun!");
}
}
// structs2.rs
// Address all the TODOs to make the tests pass!
#[derive(Debug)]
struct Order {
name: String,
year: u32,
made_by_phone: bool,
made_by_mobile: bool,
made_by_email: bool,
item_number: u32,
count: u32,
}
fn create_order_template() -> Order {
Order {
name: String::from("Bob"),
year: 2019,
made_by_phone: false,
made_by_mobile: false,
made_by_email: true,
item_number: 123,
count: 0,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn your_order() {
let order_template = create_order_template();
// TODO: Create your own order using the update syntax and template above!
let your_order = Order{name: String::from("Hacker in Rust"), count: 1, ..order_template};
assert_eq!(your_order.name, "Hacker in Rust");
assert_eq!(your_order.year, order_template.year);
assert_eq!(your_order.made_by_phone, order_template.made_by_phone);
assert_eq!(your_order.made_by_mobile, order_template.made_by_mobile);
assert_eq!(your_order.made_by_email, order_template.made_by_email);
assert_eq!(your_order.item_number, order_template.item_number);
assert_eq!(your_order.count, 1);
}
}
// structs3.rs
// Structs contain data, but can also have logic. In this exercise we have
// defined the Package struct and we want to test some logic attached to it.
// Make the code compile and the tests pass!
// If you have issues execute `rustlings hint structs3`
#[derive(Debug)]
struct Package {
sender_country: String,
recipient_country: String,
weight_in_grams: i32,
}
impl Package {
fn new(sender_country: String, recipient_country: String, weight_in_grams: i32) -> Package {
if weight_in_grams <= 0 {
// Something goes here...
panic!("bad parameter")
} else {
return Package {
sender_country,
recipient_country,
weight_in_grams,
};
}
}
fn is_international(&self) -> bool {
// Something goes here...
!self.sender_country.eq(&self.recipient_country)
}
fn get_fees(&self, cents_per_gram: i32) -> i32 {
// Something goes here...
cents_per_gram * self.weight_in_grams
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[should_panic]
fn fail_creating_weightless_package() {
let sender_country = String::from("Spain");
let recipient_country = String::from("Austria");
Package::new(sender_country, recipient_country, -2210);
}
#[test]
fn create_international_package() {
let sender_country = String::from("Spain");
let recipient_country = String::from("Russia");
let package = Package::new(sender_country, recipient_country, 1200);
assert!(package.is_international());
}
#[test]
fn create_local_package() {
let sender_country = String::from("Canada");
let recipient_country = sender_country.clone();
let package = Package::new(sender_country, recipient_country, 1200);
assert!(!package.is_international());
}
#[test]
fn calculate_transport_fees() {
let sender_country = String::from("Spain");
let recipient_country = String::from("Spain");
let cents_per_gram = 3;
let package = Package::new(sender_country, recipient_country, 1500);
assert_eq!(package.get_fees(cents_per_gram), 4500);
}
}
# Tests
Going out of order from the book to cover tests -- many of the following exercises will ask you to make tests pass!
## Further information
- [Writing Tests](https://doc.rust-lang.org/book/ch11-01-writing-tests.html)
// tests1.rs
// 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 tests1
// This test has a problem with it -- make the test compile! Make the test
// pass! Make the test fail! Execute `rustlings hint tests1` for hints :)
#[cfg(test)]
mod tests {
#[test]
fn you_can_assert() {
assert!(true);
}
}
// tests2.rs
// This test has a problem with it -- make the test compile! Make the test
// pass! Make the test fail! Execute `rustlings hint tests2` for hints :)
#[cfg(test)]
mod tests {
#[test]
fn you_can_assert_eq() {
assert_eq!(true,true);
}
}
// tests3.rs
// This test isn't testing our function -- make it do that in such a way that
// the test passes. Then write a second test that tests whether we get the result
// we expect to get when we call `is_even(5)`.
// Execute `rustlings hint tests3` for hints :)
pub fn is_even(num: i32) -> bool {
num % 2 == 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_true_when_even() {
assert!(!is_even(11));
}
#[test]
fn is_false_when_odd() {
assert!(is_even(10));
}
}
# Threads
In most current operating systems, an executed program’s code is run in a process, and the operating system manages multiple processes at once.
Within your program, you can also have independent parts that run simultaneously. The features that run these independent parts are called threads.
## Further information
- [Dining Philosophers example](https://doc.rust-lang.org/1.4.0/book/dining-philosophers.html)
- [Using Threads to Run Code Simultaneously](https://doc.rust-lang.org/book/ch16-01-threads.html)
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册