// TODO: Here are a bunch of values - some are `String`, some are `&str`. // Your task is to replace `placeholder(…)` with either `string_slice(…)` // or `string(…)` depending on what you think each value is. fnmain() { placeholder("blue");
// WARNING: This is byte indexing, not character indexing. // Character indexing can be done using `s.chars().nth(INDEX)`. placeholder(&String::from("abc")[0..1]);
// We're collecting different fruits to bake a delicious fruit cake. For this, // we have a basket, which we'll represent in the form of a hash map. The key // represents the name of each fruit we collect and the value represents how // many of that particular fruit we have collected. Three types of fruits - // Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You // must add fruit to the basket so that there is at least one of each kind and // more than 11 in total - we have a lot of mouths to feed. You are not allowed // to insert any more of the fruits that are already in the basket (Apple, // Mango, and Lychee).
forfruitin fruit_kinds { // TODO: Insert new fruits if they are not already present in the // basket. Note that you are not allowed to put any type of fruit that's // already present! } }
fnbuild_scores_table(results: &str) -> HashMap<&str, TeamScores> { // The name of the team is the key and its associated struct is the value. letmut scores = HashMap::<&str, TeamScores>::new();
forlinein results.lines() { letmut split_iterator = line.split(','); // NOTE: We use `unwrap` because we didn't deal with error handling yet. letteam_1_name = split_iterator.next().unwrap(); letteam_2_name = split_iterator.next().unwrap(); letteam_1_score: u8 = split_iterator.next().unwrap().parse().unwrap(); letteam_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();
// TODO: Populate the scores table with the extracted details. // Keep in mind that goals scored by team 1 will be the number of goals // conceded by team 2. Similarly, goals scored by team 2 will be the // number of goals conceded by team 1. }
// Insert the default with zeros if a team doesn't exist yet. letteam_1 = scores.entry(team_1_name).or_default(); // Update the values. team_1.goals_scored += team_1_score; team_1.goals_conceded += team_2_score;
// Similarly for the second team. letteam_2 = scores.entry(team_2_name).or_default(); team_2.goals_scored += team_2_score; team_2.goals_conceded += team_1_score;
// This function returns how much ice cream there is left in the fridge. // If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00, // someone eats it all, so no ice cream is left (value 0). Return `None` if // `hour_of_day` is higher than 23. fnmaybe_ice_cream(hour_of_day: u16) ->Option<u16> { // TODO: Complete the function body. }
// TODO: Fix the compiler error by adding something to this match statement. match optional_point { Some(p) => println!("Coordinates are {},{}", p.x, p.y), _ => panic!("No match!"), }
println!("{optional_point:?}"); // Don't change this line. }
编译器提示秒了:
1 2 3 4
match optional_point { Some(ref p) => println!("Coordinates are {},{}", p.x, p.y), _ => panic!("No match!"), }
ref 关键字可以让我们在模式匹配中获取引用,而不是值的所有权,类似 &。
正经来说,& 是一个表达式运算符,用于在代码中创建一个引用。ref 是一个模式关键字,用于在 match 模式中绑定一个引用。
不过标答还给了一种解法,将 & 加在 optional_point 上:
1 2 3 4
match &optional_point { Some(p) => println!("Coordinates are {},{}", p.x, p.y), _ => panic!("No match!"), }
implPositiveNonzeroInteger { fnnew(value: i64) ->Result<Self, CreationError> { // TODO: This function shouldn't always return an `Ok`. // Read the tests below to clarify what should be returned. Ok(Self(value asu64)) } }
// `Vec<T>` is generic over the type `T`. In most cases, the compiler is able to // infer `T`, for example after pushing a value with a concrete type to the vector. // But in this exercise, the compiler needs some help through a type annotation.
fnmain() { // TODO: Fix the compiler error by annotating the type of the vector // `Vec<T>`. Choose `T` as some integer type that can be created from // `u8` and `i8`. letmut numbers = Vec::new();
the trait bound `i8: std::convert::From<u8>` is not satisfied the trait `From<u8>` is not implemented for `i8` but trait `From<bool>` is implemented for it for that trait implementation, expected `bool`, found `u8` required for `u8` to implement `std::convert::Into<i8>`
// Conversion traits for primitive integer and float types // Conversions T -> T are covered by a blanket impl and therefore excluded // Some conversions from and to usize/isize are not implemented due to portability concerns macro_rules! impl_from { ... ($Small:ty => $Large:ty, #[$attr:meta], $doc:expr $(,)?) => { #[$attr] implFrom<$Small> for $Large { // Rustdocs on the impl block show a "[+] show undocumented items" toggle. // Rustdocs on functions do not. #[doc = $doc] #[inline(always)] fnfrom(small: $Small) ->Self { small asSelf } } }; }
// signed integer -> signed integer impl_from!(i8 => i16, #[stable(feature = "lossless_int_conv", since = "1.5.0")]); ...
// unsigned integer -> signed integer impl_from!(u8 => i16, #[stable(feature = "lossless_int_conv", since = "1.5.0")]); ...
// TODO: Fix the compiler error by only changing the signature of this function. fncompare_license_types(software1: ???, software2: ???) ->bool { software1.licensing_info() == software2.licensing_info() }
// TODO: Apply the `capitalize_first` function to a slice of string slices. // Return a vector of strings. // ["hello", "world"] -> ["Hello", "World"] fncapitalize_words_vector(words: &[&str]) ->Vec<String> { // ??? }
// TODO: Apply the `capitalize_first` function again to a slice of string // slices. Return a single string. // ["hello", " ", "world"] -> "Hello World" fncapitalize_words_string(words: &[&str]) ->String { // ??? }
a value of type `&str` cannot be built from an iterator over elements of type `char` the trait `std::iter::FromIterator<char>` is not implemented for `&str`
fnfactorial(num: u64) ->u64 { // TODO: Complete this function to return the factorial of `num` which is // defined as `1 * 2 * 3 * … * num`. // https://en.wikipedia.org/wiki/Factorial // // Do not use: // - early returns (using the `return` keyword explicitly) // Try not to use: // - imperative style loops (for/while) // - additional variables // For an extra challenge, don't use: // - recursion }
即不用 return, for, while,也不用额外变量和递归来计算阶乘。
看到阶乘其实我第一想法就是递归,不过看到额外说了不能用递归。于是我想了一下,写了下面的代码:
(2..=num).reduce(|x, y| x * y).unwrap_or(1)
现学现用,我也会用 Range 了。感觉这一行这么短无敌了,估计就是标答了吧。
不过一开始是 (1..=num),然后对 num 也有 if-else 判断。但后面看了看原来可以直接返回空 Range 的,所以就改成了 unwrap_or(1),这样就可以一行完成了。
pubtraitIterator { /// Reduces the elements to a single one, by repeatedly applying a reducing /// operation. fnreduce<F>(mutself, f: F) ->Option<Self::Item> where Self: Sized, F: FnMut(Self::Item, Self::Item) ->Self::Item, { letfirst = self.next()?; Some(self.fold(first, f)) } }
this `.fold` can be written more succinctly using another method
不过 reduce 就没有这个待遇,我查了一下甚至没查到一个规则名带有 reduce。
那么再来看看 product:
1 2 3 4 5 6 7 8 9 10
pubtraitIterator { /// Iterates over the entire iterator, multiplying all the elements fnproduct<P>(self) -> P where Self: Sized, P: Product<Self::Item>, { Product::product(self) } }
pubtraitProduct<A = Self>: Sized { /// Takes an iterator and generates `Self` from the elements by multiplying /// the items. fnproduct<I: Iterator<Item = A>>(iter: I) ->Self; }
// TODO: Implement the functionality of `count_for` but with an iterator instead // of a `for` loop. fncount_iterator(map: &HashMap<String, Progress>, value: Progress) ->usize { // `map` is a hash map with `String` keys and `Progress` values. // map = { "variables1": Complete, "from_str": None, … } }
// 标答 collection .iter() .flat_map(HashMap::values) // or just `.flatten()` when wanting the default iterator (`HashMap::iter`) .filter(|val| **val == value) .count()
pubtraitIterator { /// Creates an iterator that works like map, but flattens nested structure. fnflat_map<U, F>(self, f: F) -> FlatMap<Self, U, F> where Self: Sized, U: IntoIterator, F: FnMut(Self::Item) -> U, { FlatMap::new(self, f) } }
然后:
1 2 3 4 5 6 7
impl<I: Iterator, U: IntoIterator, F: FnMut(I::Item) -> U> FlatMap<I, U, F> { pub(in crate::iter) fnnew(iter: I, f: F) -> FlatMap<I, U, F> { FlatMap { inner: FlattenCompat::new(iter.map(f)) } }
... }
下面就不深究了,看一下 flatten:
1 2 3 4 5 6 7 8 9 10
pubtraitIterator { /// Creates an iterator that flattens nested structure. fnflatten(self) -> Flatten<Self> where Self: Sized, Self::Item: IntoIterator, { Flatten::new(self) } }
fnmain() { // `Arc` isn't enough if you want a **mutable** shared state. // We need to wrap the value with a `Mutex`. letstatus = Arc::new(Mutex::new(JobStatus { jobs_done: 0 })); // ^^^^^^^^^^^ ^
impl<T: ?Sized> Mutex<T> { /// Acquires a mutex, blocking the current thread until it is able to do so. pubfnlock(&self) -> LockResult<MutexGuard<'_, T>> { unsafe { self.inner.lock(); MutexGuard::new(self) } }
... }
...
/// A type alias for the result of a lock method which can be poisoned. pubtypeLockResult<T> = Result<T, PoisonError<T>>;
...
/// A type of error which can be returned whenever a lock is acquired. pubstructPoisonError<T> { data: T, #[cfg(not(panic = "unwind"))] _never: !, }
这里涉及了 MutexGuard,下面还有 Drop 的实现,以实现解锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/// An RAII implementation of a "scoped lock" of a mutex. When this structure is /// dropped (falls out of scope), the lock will be unlocked. pubstructMutexGuard<'a, T: ?Sized + 'a> { lock: &'a Mutex<T>, poison: poison::Guard, }
const UNLOCKED: State = 0; const LOCKED: State = 1; // locked, no other threads waiting const CONTENDED: State = 2; // locked, and other threads waiting (contended)
fnlock_contended(&self) { // Spin first to speed things up if the lock is released quickly. letmut state = self.spin();
// If it's unlocked now, attempt to take the lock // without marking it as contended. if state == UNLOCKED { matchself.futex.compare_exchange(UNLOCKED, LOCKED, Acquire, Relaxed) { Ok(_) => return, // Locked! Err(s) => state = s, } }
loop { // Put the lock in contended state. // We avoid an unnecessary write if it as already set to CONTENDED, // to be friendlier for the caches. if state != CONTENDED && self.futex.swap(CONTENDED, Acquire) == UNLOCKED { // We changed it from UNLOCKED to CONTENDED, so we just successfully locked it. return; }
// Wait for the futex to change state, assuming it is still CONTENDED. futex_wait(&self.futex, CONTENDED, None);
// Spin again after waking up. state = self.spin(); } }
pubunsafefnunlock(&self) { ifself.futex.swap(UNLOCKED, Release) == CONTENDED { // We only wake up one thread. When that thread locks the mutex, it // will mark the mutex as CONTENDED (see lock_contended above), // which makes sure that any other waiting threads will also be // woken up eventually. self.wake(); } }
// TODO: Fix the compiler error without taking the macro definition out of this // module. mod macros { #[macro_export] macro_rules! my_macro { () => { println!("Check out my macro!"); }; } }
fnmain() { my_macro!(); }
导出宏用 #[macro_export] 属性,之前正好见过,不然我也不知道咋整。
第 4 题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// TODO: Fix the compiler error by adding one or two characters. #[rustfmt::skip] macro_rules! my_macro { () => { println!("Check out my macro!"); }; ($val:expr) => { println!("Look at this other macro: {}", $val); } }
// Here are some more easy Clippy fixes so you can see its utility 📎 // TODO: Fix all the Clippy lints.
#[rustfmt::skip] #[allow(unused_variables, unused_assignments)] fnmain() { letmy_option: Option<&str> = None; // Assume that you don't know the value of `my_option`. // In the case of `Some`, we want to print its value. if my_option.is_none() { println!("{}", my_option.unwrap()); }
letmy_arr = &[ -1, -2, -3 -4, -5, -6 ]; println!("My array! Here it is: {my_arr:?}");
#[rustfmt::skip] #[allow(unused_variables, unused_assignments)] fnmain() { letmy_option: Option<&str> = None; // `unwrap` of an `Option` after checking if it is `None` will panic. // Use `if-let` instead. ifletSome(value) = my_option { println!("{value}"); }
// A comma was missing. letmy_arr = &[ -1, -2, -3, -4, -5, -6, ]; println!("My array! Here it is: {my_arr:?}");
letmut my_empty_vec = vec![1, 2, 3, 4, 5]; // `resize` mutates a vector instead of returning a new one. // `resize(0, …)` clears a vector, so it is better to use `clear`. my_empty_vec.clear(); println!("This Vec is empty, see? {my_empty_vec:?}");
letmut value_a = 45; letmut value_b = 66; // Use `mem::swap` to correctly swap two values. mem::swap(&mut value_a, &mut value_b); println!("value a: {value_a}; value b: {value_b}"); }
分别是 if-let,.clear() 与 mem::swap。
第一个我有点晕,写成了:
1 2 3
if my_option.is_some() { println!("{:?}", my_option); }
// We implement the Default trait to use it as a fallback when the provided // string is not convertible into a `Person` object. implDefaultforPerson { fndefault() ->Self { Self { name: String::from("John"), age: 30, } } }
// TODO: Complete this `From` implementation to be able to parse a `Person` // out of a string in the form of "Mark,20". implFrom<&str> forPerson { fnfrom(s: &str) ->Self {} }
impl<T> [T] { /// Returns the first element of the slice, or `None` if it is empty. pubconstfnfirst(&self) ->Option<&T> { iflet [first, ..] = self { Some(first) } else { None } } }
居然不是用 self.get(0) 实现的,而是用模式匹配。
标答用了模式匹配,有点聪明啊,我怎么想不到呢:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
letmut split = s.split(','); let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else { // ^^^^ there should be no third element returnSelf::default(); };
// We will use this error type for the `TryFrom` conversions. #[derive(Debug, PartialEq)] enumIntoColorError { // Incorrect length of slice BadLen, // Integer conversion error IntConversion, }
// TODO: Tuple implementation. // Correct RGB color values must be integers in the 0..=255 range. implTryFrom<(i16, i16, i16)> forColor { typeError = IntoColorError;
// Obtain the number of bytes (not characters) in the given argument // (`.len()` returns the number of bytes in a string). // TODO: Add the `AsRef` trait appropriately as a trait bound. fnbyte_counter<T>(arg: T) ->usize { arg.as_ref().len() }
// Obtain the number of characters (not bytes) in the given argument. // TODO: Add the `AsRef` trait appropriately as a trait bound. fnchar_counter<T>(arg: T) ->usize { arg.as_ref().chars().count() }
// Squares a number using `as_mut()`. // TODO: Add the appropriate trait bound. fnnum_sq<T>(arg: &mut T) { // TODO: Implement the function body. }