fnmain(){ let a = String::from("Hello Rust"); let b = a;//此时所有权转移到b println!("{:?}", a); //Wrong !!! let c = b.clone();//c通过克隆产生,不会拿走b所有权 { let d = b;//d获得b的所有权,并在该代码段结束后drop } println!("{:?}", b); //Wrong !!! }
pub struct Adapter<T: Driver>(T);
-
-impl<T: Driver> driver::DriverOps for Adapter<T> {
-type RegType = bindings::pci_driver;
-
-unsafe fn register(
- reg: *mut bindings::pci_driver,
-) -> Result {
- // SAFETY: By the safety requirements of this function (defined in the trait definition),
- // `reg` is non-null and valid.
- let pdrv: &mut bindings::pci_driver = unsafe { &mut *reg };
-
- pdrv.name = name.as_char_ptr();
-
- //...
-}
-}
-
-
//linux/rust/kernel/net.rs
-
-unsafe extern "C" fn get_stats64_callback(
- netdev: *mut bindings::net_device,
- storage: *mut bindings::rtnl_link_stats64,
-) {
- // SAFETY: The C API guarantees that `net_device` isn't released while this function is running.
- let dev = unsafe { Device::from_ptr(netdev) };
-}
-
-
-impl Device {
-/// # Safety
-///
-/// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
-/// returned [`Device`] instance.
-pub(crate) unsafe fn from_ptr<'a>(ptr: *const bindings::net_device) -> &'a Device {
- // SAFETY: The safety requirements guarantee the validity of the dereference, while the
- // `Device` type being transparent makes the cast ok.
- unsafe { &*ptr.cast() }
-}
-}
-
ref to raw pointer
-
//linux/rust/kernel/net.rs
-
-impl<T: NapiPoller> NapiAdapter<T> {
-/// Creates a new Napi object.
-pub fn add_weight(dev: &Device, weight: i32) -> Result<Pin<UniqueArc<Napi>>> {
- let mut napi = Pin::from(UniqueArc::try_new(Napi::new())?);
-
- unsafe {
- bindings::netif_napi_add_weight(
- &*dev as *const Device as *mut bindings::net_device,
- napi.as_mut().0.get(),
- Some(Self::poll_callback),
- weight,
- )
- }
- Ok(napi)
-}
-}
-
&[u8] to core::ffi::cchar
-
-
-
#[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))]
-unsafe fn printk(&self, klevel: &[u8], msg: fmt::Arguments<'_>) {
- // SAFETY: `klevel` is null-terminated and one of the kernel constants. `self.raw_device`
- // is valid because `self` is valid. The "%pA" format string expects a pointer to
- // `fmt::Arguments`, which is what we're passing as the last argument.
- #[cfg(CONFIG_PRINTK)]
- unsafe {
- bindings::_dev_printk(
- klevel as *const _ as *const core::ffi::c_char,
- self.raw_device(),
- c_str!("%pA").as_char_ptr(),
- &msg as *const _ as *const core::ffi::c_void,
- )
- };
-}
-
raw pointer to box
/// # Safety
-///
-/// The `ptr` must contain an owned box of `Foo`.
-unsafe fn raw_pointer_to_box(ptr: *mut Foo) -> Box<Foo> {
- // SAFETY: The `ptr` contains an owned box of `Foo` by contract. We
- // simply reconstruct the box from that pointer.
- let mut ret: Box<Foo> = unsafe { Box::from_raw(ptr) };
- ret
-}
-
build.rs
set env
-
let timestamp = std::time::SystemTime::now()
- .duration_since(std::time::UNIX_EPOCH)
- .unwrap()
- .as_secs(); // What's the use of this timestamp here?
-let your_command = format!(
- "rustc-env=TEST_FOO={}",
- timestamp
-);
-println!("cargo:{}", your_command);
set feature
-
// In tests8, we should enable "pass" feature to make the
-// testcase return early. Fill in the command to tell
-// Cargo about that.
-let your_command = "rustc-cfg=feature=\"pass\"";
-println!("cargo:{}", your_command);
pub struct Adapter<T: Driver>(T);
+
+impl<T: Driver> driver::DriverOps for Adapter<T> {
+type RegType = bindings::pci_driver;
+
+unsafe fn register(
+ reg: *mut bindings::pci_driver,
+) -> Result {
+ // SAFETY: By the safety requirements of this function (defined in the trait definition),
+ // `reg` is non-null and valid.
+ let pdrv: &mut bindings::pci_driver = unsafe { &mut *reg };
+
+ pdrv.name = name.as_char_ptr();
+
+ //...
+}
+}
+
+
//linux/rust/kernel/net.rs
+
+unsafe extern "C" fn get_stats64_callback(
+ netdev: *mut bindings::net_device,
+ storage: *mut bindings::rtnl_link_stats64,
+) {
+ // SAFETY: The C API guarantees that `net_device` isn't released while this function is running.
+ let dev = unsafe { Device::from_ptr(netdev) };
+}
+
+
+impl Device {
+/// # Safety
+///
+/// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
+/// returned [`Device`] instance.
+pub(crate) unsafe fn from_ptr<'a>(ptr: *const bindings::net_device) -> &'a Device {
+ // SAFETY: The safety requirements guarantee the validity of the dereference, while the
+ // `Device` type being transparent makes the cast ok.
+ unsafe { &*ptr.cast() }
+}
+}
+
ref to raw pointer
+
//linux/rust/kernel/net.rs
+
+impl<T: NapiPoller> NapiAdapter<T> {
+/// Creates a new Napi object.
+pub fn add_weight(dev: &Device, weight: i32) -> Result<Pin<UniqueArc<Napi>>> {
+ let mut napi = Pin::from(UniqueArc::try_new(Napi::new())?);
+
+ unsafe {
+ bindings::netif_napi_add_weight(
+ &*dev as *const Device as *mut bindings::net_device,
+ napi.as_mut().0.get(),
+ Some(Self::poll_callback),
+ weight,
+ )
+ }
+ Ok(napi)
+}
+}
+
&[u8] to core::ffi::cchar
+
+
+
#[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))]
+unsafe fn printk(&self, klevel: &[u8], msg: fmt::Arguments<'_>) {
+ // SAFETY: `klevel` is null-terminated and one of the kernel constants. `self.raw_device`
+ // is valid because `self` is valid. The "%pA" format string expects a pointer to
+ // `fmt::Arguments`, which is what we're passing as the last argument.
+ #[cfg(CONFIG_PRINTK)]
+ unsafe {
+ bindings::_dev_printk(
+ klevel as *const _ as *const core::ffi::c_char,
+ self.raw_device(),
+ c_str!("%pA").as_char_ptr(),
+ &msg as *const _ as *const core::ffi::c_void,
+ )
+ };
+}
+
raw pointer to box
/// # Safety
+///
+/// The `ptr` must contain an owned box of `Foo`.
+unsafe fn raw_pointer_to_box(ptr: *mut Foo) -> Box<Foo> {
+ // SAFETY: The `ptr` contains an owned box of `Foo` by contract. We
+ // simply reconstruct the box from that pointer.
+ let mut ret: Box<Foo> = unsafe { Box::from_raw(ptr) };
+ ret
+}
+
build.rs
set env
+
let timestamp = std::time::SystemTime::now()
+ .duration_since(std::time::UNIX_EPOCH)
+ .unwrap()
+ .as_secs(); // What's the use of this timestamp here?
+let your_command = format!(
+ "rustc-env=TEST_FOO={}",
+ timestamp
+);
+println!("cargo:{}", your_command);
set feature
+
// In tests8, we should enable "pass" feature to make the
+// testcase return early. Fill in the command to tell
+// Cargo about that.
+let your_command = "rustc-cfg=feature=\"pass\"";
+println!("cargo:{}", your_command);
error[E0282]: type annotations needed for `Vec<_>` --> exercises/variables/variables1.rs:10:9 | 10 | let v = vec![]; | ^ ------ type must be known at this point | help: consider giving `v` an explicit type, where the placeholders `_` are specified | 10 | let v: Vec<_> = vec![]; | ++++++++
error[E0282]: type annotations needed for `Vec<_>` --> exercises/variables/variables1.rs:10:9 | 10 | let v = vec![]; | ^ ------ type must be known at this point | help: consider giving `v` an explicit type, where the placeholders `_` are specified | 10 | let v: Vec<_> = vec![]; | ++++++++
letmut v = vec![1, 2, 3]; for i in v.iter_mut() { *i += 1; }
-
-
move_semantics2.rs
1 2 3 4 5 6 7 8
// move_semantics2.rs // // Expected output: // vec0 has length 3, with contents `[22, 44, 66]` // vec1 has length 4, with contents `[22, 44, 66, 88]` // // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand // for a hint.
// move_semantics4.rs // // Refactor this code so that instead of passing `vec0` into the `fill_vec` // function, the Vector gets created in the function itself and passed back to // the main function. // // Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand // for a hint.
fnmain() { // let vec0 = Vec::new();
letmut 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 fnfill_vec() -> Vec<i32> { letmut vec = Vec::new();
Unlike functions, methods are defined within the context of a struct,and their first parameter is always self, which represents the instance of the struct the method is being called on.
-
-
we still need to use the & in front of the self shorthand to indicate that this method borrows the Self instance, just as we did in rectangle: &Rectangle. Methods can take ownership of self, borrow selfimmutably, as we’ve done here, or borrow selfmutably, just as they can any other parameter.
// 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! // // Execute `rustlings hint structs3` or use the `hint` watch subcommand for a // hint.
// enums3.rs // // Address all the TODOs to make the tests pass! // // Execute `rustlings hint enums3` or use the `hint` watch subcommand for a // hint.
// I AM NOT DONE
enumMessage { // TODO: implement the message variant types based on their usage below ChangeColor(u8, u8, u8), Echo(String), Move(Point), Quit }
fnprocess(&mutself, message: Message) { // TODO: create a match expression to process the different message // variants // Remember: When passing a tuple as a function argument, you'll need // extra parentheses: fn function((t, u, p, l, e)) // 模式匹配message match message { // 结构枚举类型绑定的值,相当于还完成了语句:Message::ChangeColor(r,g,b)=message Message::ChangeColor(r, g, b) => { self.change_color((r, g, b)); } Message::Echo(text) => { self.echo(text); }, Message::Move(point) => { self.move_position(point); } Message::Quit => { self.quit(); } } } }
#[test] fnreplace_a_string() { assert_eq!(replace_me("I think cars are cool"), "I think balloons are cool"); assert_eq!(replace_me("I love to look at cars"), "I love to look at balloons"); } }
-
-
hashmaps2.rs
问题1:
-
关于hashmap的更新实例中:为什么修改count的值就可以修改hashmap的键值呢?
-
1 2 3 4 5 6 7 8 9 10 11 12
use std::collections::HashMap;
let text = "hello world wonderful world";
letmut map = HashMap::new(); // 根据空格来切分字符串(英文单词都是通过空格切分) for word in text.split_whitespace() { let count = map.entry(word).or_insert(0); *count += 1; }
// quiz2.rs // // This is a quiz for the following sections: // - Strings // - Vecs // - Move semantics // - Modules // - Enums // // Let's build a little machine in the form of a function. As input, we're going // to give a list of strings and commands. These commands determine what action // is going to be applied to the string. It can either be: // - 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. // // No hints this time!
// options2.rs // // Execute `rustlings hint options2` or use the `hint` watch subcommand for a // hint.
#[cfg(test)] mod tests { #[test] fnsimple_option() { let target = "rustlings"; let optional_target = Some(target);
// TODO: Make this an if let statement whose value is "Some" type ifletSome(word) = optional_target { assert_eq!(word, target); } }
#[test] fnlayered_option() { let range = 10; letmut optional_integers: Vec<Option<i8>> = vec![None];
for i in1..(range + 1) { optional_integers.push(Some(i)); }
letmut cursor = range;
// 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. whileletSome(Some(integer)) = optional_integers.pop() { assert_eq!(integer, cursor); cursor -= 1; }
// 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 cost of the 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` or use the `hint` watch subcommand for a // hint.
// I AM NOT DONE
use std::num::ParseIntError;
pubfntotal_cost(item_quantity: &str) -> Result<i32, ParseIntError> { let processing_fee = 1; let cost_per_item = 5; let qty = item_quantity.parse::<i32>(); // 如果parse()转换成功,qty将会是一个Result::Ok(qty) // 如果parse()转换失败,qty将会是一个Result::Err(ParseIntError) match qty { Ok(qty) => Ok(qty * cost_per_item + processing_fee), Err(ParseIntError) => Err(ParseIntError) } }
// 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` or use the `hint` watch subcommand for a // hint.
fnnew(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 asu64)), } }
-
-
但感觉这里也可以直接if判断:
-
1 2 3 4 5 6 7
if value > 0 { Ok(PositiveNonzeroInteger(value asu64)) } elseif value < 0 { Err(CreationError::Negative) } else { Err(CreationError::Zero) }
// errors6.rs // // Using catch-all error types like `Box<dyn error::Error>` isn't recommended // for library code, where callers might want to make decisions based on the // error content, instead of printing it out or propagating it further. Here, we // define a custom error type to make it possible for callers to decide what to // do next when our function returns an error. // // Execute `rustlings hint errors6` or use the `hint` watch subcommand for a // hint.
use std::num::ParseIntError;
// This is a custom error type that we will be using in `parse_pos_nonzero()`. #[derive(PartialEq, Debug)] enumParsePosNonzeroError { Creation(CreationError), ParseInt(ParseIntError), }
fnparse_pos_nonzero(s: &str) -> Result<PositiveNonzeroInteger, ParsePosNonzeroError> { // TODO: change this to return an appropriate error instead of panicking // when `parse()` returns an error.
impl PositiveNonzeroInteger { fnnew(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 asu64)), } } }
#[cfg(test)] mod test { use super::*;
#[test] fntest_parse_error() { // We can't construct a ParseIntError, so we have to pattern match. assert!(matches!( parse_pos_nonzero("not a number"), Err(ParsePosNonzeroError::ParseInt(_)) )); }
// generics2.rs // // This powerful wrapper provides the ability to store a positive integer value. // Rewrite it using generics so that it supports wrapping ANY type. // // Execute `rustlings hint generics2` or use the `hint` watch subcommand for a // hint.
// traits5.rs // // Your task is to replace the '??' sections so the code compiles. // // Don't change any line other than the marked one. // // Execute `rustlings hint traits5` or use the `hint` watch subcommand for a // hint.
// quiz3.rs // // This quiz tests: // - Generics // - Traits // // An imaginary magical school has a new report card generation system written // in Rust! Currently the system only supports creating report cards where the // student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the // school also issues alphabetical grades (A+ -> F-) and needs to be able to // print both types of report card! // // Make the necessary code changes in the struct ReportCard and the impl block // to support alphabetical report cards. Change the Grade in the second test to // "A+" to show that your changes allow alphabetical grades. // // Execute `rustlings hint quiz3` or use the `hint` watch subcommand for a hint.
#[test] fngenerate_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] fngenerate_alphabetic_report_card() { // TODO: Make sure to change the grade here after you finish the exercise. let report_card = ReportCard { grade: String::from("A+"), student_name: "Gary Plotter".to_string(), student_age: 11, }; assert_eq!( report_card.print(), "Gary Plotter (11) - achieved a grade of A+" ); } }
fnmain() { let novel = String::from("Call me Ishmael. Some years ago..."); let first_sentence = novel.split('.').next().expect("Could not find a '.'"); let i = ImportantExcerpt { part: first_sentence, }; }
pubfnfactorial(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. (1..=num).product() }
fnmain() { // 创建handles,用以存储后面10个线程的句柄 letmut handles = vec![]; // 通过循环创建了10个线程,每个线程会执行一个闭包 for i in0..10 { // 使用move拿走当前i的所有权 handles.push(thread::spawn(move || { let start = Instant::now(); thread::sleep(Duration::from_millis(250)); // 250ms后输出,因为i的所有权被拿到了,因此可以访问i println!("thread {} is complete", i); start.elapsed().as_millis() })); } // 创建results,用以存储10个线程的执行时间 letmut results: Vec<u128> = vec![]; for handle in handles { // TODO: a struct is returned from thread::spawn, can you use it? // 等待每个线程执行完成再结束主线程 results.push(handle.join().unwrap()); } // 输出时间 for (i, result) in results.into_iter().enumerate() { println!("thread {} took {}ms", i, result); } }
fnsend_tx(q: Queue, tx: mpsc::Sender<u32>) -> () { let qc = Arc::new(q); let qc1 = Arc::clone(&qc); let qc2 = Arc::clone(&qc); // 克隆发送方 let tx_clone = tx.clone();
thread::spawn(move || { for val in &qc1.first_half { println!("sending {:?}", val); tx.send(*val).unwrap(); thread::sleep(Duration::from_secs(1)); } });
thread::spawn(move || { for val in &qc2.second_half { println!("sending {:?}", val); // 使用克隆的发送方 tx_clone.send(*val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); }
#[rustfmt::skip] macro_rules! my_macro { () => { println!("Check out my macro!"); }; ($val:expr) => { println!("Look at this other macro: {}", $val); }; }
fnmain() { my_macro!(); my_macro!(7777); }
-
-
匹配多次:
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#[rustfmt::skip] macro_rules! my_macro { () => { println!("Check out my macro!"); }; ($($val:expr),*) => { $( println!("Look at this other macro: {}", $val); )* }; }
// Insert a value into the BST fninsert(&mutself, value: T) { // 直接使用节点的插入方法 match &mutself.root { Some(root_node) => { root_node.insert(value); } _ => { self.root = Some(Box::new(TreeNode::new(value))); } } }
-
-
1 2 3 4 5 6 7
// Search for a value in the BST fnsearch(&self, value: T) -> bool { match &self.root { Some(root_node) => { root_node.search(value) } _ => { false } } }
// 按堆规定的顺序返回两个孩子中应该在前面的一个的索引值 fnsmallest_child_idx(&self, idx: usize) -> usize { let left = self.left_child_idx(idx); let right = self.right_child_idx(idx); if right > self.count { left } elseif (self.comparator)(&self.items[left], &self.items[right]) { left } else { right } } }
letmut v = vec![1, 2, 3]; for i in v.iter_mut() { *i += 1; }
+
+
move_semantics2.rs
1 2 3 4 5 6 7 8
// move_semantics2.rs // // Expected output: // vec0 has length 3, with contents `[22, 44, 66]` // vec1 has length 4, with contents `[22, 44, 66, 88]` // // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand // for a hint.
// move_semantics4.rs // // Refactor this code so that instead of passing `vec0` into the `fill_vec` // function, the Vector gets created in the function itself and passed back to // the main function. // // Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand // for a hint.
fnmain() { // let vec0 = Vec::new();
letmut 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 fnfill_vec() -> Vec<i32> { letmut vec = Vec::new();
Unlike functions, methods are defined within the context of a struct,and their first parameter is always self, which represents the instance of the struct the method is being called on.
+
+
we still need to use the & in front of the self shorthand to indicate that this method borrows the Self instance, just as we did in rectangle: &Rectangle. Methods can take ownership of self, borrow selfimmutably, as we’ve done here, or borrow selfmutably, just as they can any other parameter.
// 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! // // Execute `rustlings hint structs3` or use the `hint` watch subcommand for a // hint.
// enums3.rs // // Address all the TODOs to make the tests pass! // // Execute `rustlings hint enums3` or use the `hint` watch subcommand for a // hint.
// I AM NOT DONE
enumMessage { // TODO: implement the message variant types based on their usage below ChangeColor(u8, u8, u8), Echo(String), Move(Point), Quit }
fnprocess(&mutself, message: Message) { // TODO: create a match expression to process the different message // variants // Remember: When passing a tuple as a function argument, you'll need // extra parentheses: fn function((t, u, p, l, e)) // 模式匹配message match message { // 结构枚举类型绑定的值,相当于还完成了语句:Message::ChangeColor(r,g,b)=message Message::ChangeColor(r, g, b) => { self.change_color((r, g, b)); } Message::Echo(text) => { self.echo(text); }, Message::Move(point) => { self.move_position(point); } Message::Quit => { self.quit(); } } } }
#[test] fnreplace_a_string() { assert_eq!(replace_me("I think cars are cool"), "I think balloons are cool"); assert_eq!(replace_me("I love to look at cars"), "I love to look at balloons"); } }
+
+
hashmaps2.rs
问题1:
+
关于hashmap的更新实例中:为什么修改count的值就可以修改hashmap的键值呢?
+
1 2 3 4 5 6 7 8 9 10 11 12
use std::collections::HashMap;
let text = "hello world wonderful world";
letmut map = HashMap::new(); // 根据空格来切分字符串(英文单词都是通过空格切分) for word in text.split_whitespace() { let count = map.entry(word).or_insert(0); *count += 1; }
// quiz2.rs // // This is a quiz for the following sections: // - Strings // - Vecs // - Move semantics // - Modules // - Enums // // Let's build a little machine in the form of a function. As input, we're going // to give a list of strings and commands. These commands determine what action // is going to be applied to the string. It can either be: // - 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. // // No hints this time!
// options2.rs // // Execute `rustlings hint options2` or use the `hint` watch subcommand for a // hint.
#[cfg(test)] mod tests { #[test] fnsimple_option() { let target = "rustlings"; let optional_target = Some(target);
// TODO: Make this an if let statement whose value is "Some" type ifletSome(word) = optional_target { assert_eq!(word, target); } }
#[test] fnlayered_option() { let range = 10; letmut optional_integers: Vec<Option<i8>> = vec![None];
for i in1..(range + 1) { optional_integers.push(Some(i)); }
letmut cursor = range;
// 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. whileletSome(Some(integer)) = optional_integers.pop() { assert_eq!(integer, cursor); cursor -= 1; }
// 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 cost of the 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` or use the `hint` watch subcommand for a // hint.
// I AM NOT DONE
use std::num::ParseIntError;
pubfntotal_cost(item_quantity: &str) -> Result<i32, ParseIntError> { let processing_fee = 1; let cost_per_item = 5; let qty = item_quantity.parse::<i32>(); // 如果parse()转换成功,qty将会是一个Result::Ok(qty) // 如果parse()转换失败,qty将会是一个Result::Err(ParseIntError) match qty { Ok(qty) => Ok(qty * cost_per_item + processing_fee), Err(ParseIntError) => Err(ParseIntError) } }
// 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` or use the `hint` watch subcommand for a // hint.
fnnew(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 asu64)), } }
+
+
但感觉这里也可以直接if判断:
+
1 2 3 4 5 6 7
if value > 0 { Ok(PositiveNonzeroInteger(value asu64)) } elseif value < 0 { Err(CreationError::Negative) } else { Err(CreationError::Zero) }
// errors6.rs // // Using catch-all error types like `Box<dyn error::Error>` isn't recommended // for library code, where callers might want to make decisions based on the // error content, instead of printing it out or propagating it further. Here, we // define a custom error type to make it possible for callers to decide what to // do next when our function returns an error. // // Execute `rustlings hint errors6` or use the `hint` watch subcommand for a // hint.
use std::num::ParseIntError;
// This is a custom error type that we will be using in `parse_pos_nonzero()`. #[derive(PartialEq, Debug)] enumParsePosNonzeroError { Creation(CreationError), ParseInt(ParseIntError), }
fnparse_pos_nonzero(s: &str) -> Result<PositiveNonzeroInteger, ParsePosNonzeroError> { // TODO: change this to return an appropriate error instead of panicking // when `parse()` returns an error.
impl PositiveNonzeroInteger { fnnew(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 asu64)), } } }
#[cfg(test)] mod test { use super::*;
#[test] fntest_parse_error() { // We can't construct a ParseIntError, so we have to pattern match. assert!(matches!( parse_pos_nonzero("not a number"), Err(ParsePosNonzeroError::ParseInt(_)) )); }
// generics2.rs // // This powerful wrapper provides the ability to store a positive integer value. // Rewrite it using generics so that it supports wrapping ANY type. // // Execute `rustlings hint generics2` or use the `hint` watch subcommand for a // hint.
// traits5.rs // // Your task is to replace the '??' sections so the code compiles. // // Don't change any line other than the marked one. // // Execute `rustlings hint traits5` or use the `hint` watch subcommand for a // hint.
// quiz3.rs // // This quiz tests: // - Generics // - Traits // // An imaginary magical school has a new report card generation system written // in Rust! Currently the system only supports creating report cards where the // student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the // school also issues alphabetical grades (A+ -> F-) and needs to be able to // print both types of report card! // // Make the necessary code changes in the struct ReportCard and the impl block // to support alphabetical report cards. Change the Grade in the second test to // "A+" to show that your changes allow alphabetical grades. // // Execute `rustlings hint quiz3` or use the `hint` watch subcommand for a hint.
#[test] fngenerate_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] fngenerate_alphabetic_report_card() { // TODO: Make sure to change the grade here after you finish the exercise. let report_card = ReportCard { grade: String::from("A+"), student_name: "Gary Plotter".to_string(), student_age: 11, }; assert_eq!( report_card.print(), "Gary Plotter (11) - achieved a grade of A+" ); } }
fnmain() { let novel = String::from("Call me Ishmael. Some years ago..."); let first_sentence = novel.split('.').next().expect("Could not find a '.'"); let i = ImportantExcerpt { part: first_sentence, }; }
pubfnfactorial(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. (1..=num).product() }
fnmain() { // 创建handles,用以存储后面10个线程的句柄 letmut handles = vec![]; // 通过循环创建了10个线程,每个线程会执行一个闭包 for i in0..10 { // 使用move拿走当前i的所有权 handles.push(thread::spawn(move || { let start = Instant::now(); thread::sleep(Duration::from_millis(250)); // 250ms后输出,因为i的所有权被拿到了,因此可以访问i println!("thread {} is complete", i); start.elapsed().as_millis() })); } // 创建results,用以存储10个线程的执行时间 letmut results: Vec<u128> = vec![]; for handle in handles { // TODO: a struct is returned from thread::spawn, can you use it? // 等待每个线程执行完成再结束主线程 results.push(handle.join().unwrap()); } // 输出时间 for (i, result) in results.into_iter().enumerate() { println!("thread {} took {}ms", i, result); } }
fnsend_tx(q: Queue, tx: mpsc::Sender<u32>) -> () { let qc = Arc::new(q); let qc1 = Arc::clone(&qc); let qc2 = Arc::clone(&qc); // 克隆发送方 let tx_clone = tx.clone();
thread::spawn(move || { for val in &qc1.first_half { println!("sending {:?}", val); tx.send(*val).unwrap(); thread::sleep(Duration::from_secs(1)); } });
thread::spawn(move || { for val in &qc2.second_half { println!("sending {:?}", val); // 使用克隆的发送方 tx_clone.send(*val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); }
#[rustfmt::skip] macro_rules! my_macro { () => { println!("Check out my macro!"); }; ($val:expr) => { println!("Look at this other macro: {}", $val); }; }
fnmain() { my_macro!(); my_macro!(7777); }
+
+
匹配多次:
+
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#[rustfmt::skip] macro_rules! my_macro { () => { println!("Check out my macro!"); }; ($($val:expr),*) => { $( println!("Look at this other macro: {}", $val); )* }; }
// Insert a value into the BST fninsert(&mutself, value: T) { // 直接使用节点的插入方法 match &mutself.root { Some(root_node) => { root_node.insert(value); } _ => { self.root = Some(Box::new(TreeNode::new(value))); } } }
+
+
1 2 3 4 5 6 7
// Search for a value in the BST fnsearch(&self, value: T) -> bool { match &self.root { Some(root_node) => { root_node.search(value) } _ => { false } } }
// 按堆规定的顺序返回两个孩子中应该在前面的一个的索引值 fnsmallest_child_idx(&self, idx: usize) -> usize { let left = self.left_child_idx(idx); let right = self.right_child_idx(idx); if right > self.count { left } elseif (self.comparator)(&self.items[left], &self.items[right]) { left } else { right } } }
反汇编发现 rust 若按照第一种方式,编译器会将代码编译为两次在一个寄存器中取值,然而这个值会在 abi 调用后失效。在交流群里询问才得知需要在内联汇编上加上一行 clobber_abi(“C”),这时编译器会帮你自动加上某个 abi 的 function call 的 caller-saved 的寄存器会在内联汇编被使用的提示,从而会保证程序上下文寄存器的值在函数调用前后都有效。具体的 ref: https://doc.rust-lang.org/reference/inline-assembly.html.
反汇编发现 rust 若按照第一种方式,编译器会将代码编译为两次在一个寄存器中取值,然而这个值会在 abi 调用后失效。在交流群里询问才得知需要在内联汇编上加上一行 clobber_abi(“C”),这时编译器会帮你自动加上某个 abi 的 function call 的 caller-saved 的寄存器会在内联汇编被使用的提示,从而会保证程序上下文寄存器的值在函数调用前后都有效。具体的 ref: https://doc.rust-lang.org/reference/inline-assembly.html.
其实我学习Rust的开端比较早,就是看到各大视频网站有很多关于Rust如何安全的宣传视频,当时也没有适用场景和刚需,单纯出于好奇就开始了自学,在学完之后也就仅限于用来做一些比如Advent of Code的编程题,但是其间经常由于Rust严格的检查而被Rust折磨🤣。rCore把Rust用到了系统级的编程领域,难度更是加大了,因为要理解这些代码,首先要理解其背后隐含的OS设计思想,这些就是我对于Rust学习的感受。
其实我学习Rust的开端比较早,就是看到各大视频网站有很多关于Rust如何安全的宣传视频,当时也没有适用场景和刚需,单纯出于好奇就开始了自学,在学完之后也就仅限于用来做一些比如Advent of Code的编程题,但是其间经常由于Rust严格的检查而被Rust折磨🤣。rCore把Rust用到了系统级的编程领域,难度更是加大了,因为要理解这些代码,首先要理解其背后隐含的OS设计思想,这些就是我对于Rust学习的感受。
/// Task information pubstructTaskInfo { /// Task status in it's life cycle status: TaskStatus, /// The numbers of syscall called by task syscall_times: [u32; MAX_SYSCALL_NUM], /// Total running time of task time: usize, }
-
其内存布局是不确定的,编译器甚至不保证 status,syscall_times,time 是按顺序排布的,添加 #[repr(C)] 才能保证会是我们想象的类似 C 语言的内存布局。
/// Task information pubstructTaskInfo { /// Task status in it's life cycle status: TaskStatus, /// The numbers of syscall called by task syscall_times: [u32; MAX_SYSCALL_NUM], /// Total running time of task time: usize, }
+
其内存布局是不确定的,编译器甚至不保证 status,syscall_times,time 是按顺序排布的,添加 #[repr(C)] 才能保证会是我们想象的类似 C 语言的内存布局。
再比如编译原理课程中,我们要实现类型检查,那么很自然的就是要实现一个结构体,用以表示各种类型。在这个结构体中,一个枚举类型用以表示当前类型,一个 union 中保存了该种类型附加的信息。即使使用到了 union 这样的关键字减少冗余的部分,也并不优雅,如果换成 rust,则可以用一个枚举类型实现类型系统,相当简洁明了,且优雅。