update project structure

This commit is contained in:
sunface
2022-02-28 21:32:54 +08:00
parent 95dee9dac7
commit b8009cfe83
90 changed files with 26 additions and 30 deletions

View File

@ -1 +0,0 @@
practice.rs

Submodule en/book deleted from 036e93c39b

View File

@ -1,20 +0,0 @@
[book]
title = "Rust By Practice"
description = "Learn Rust with Example, Exercise and real Practice, written with ❤️ by https://course.rs team"
authors = ["sunface, https://im.dev"]
language = "en"
[output.html.playpen]
editable = true
editor = "ace"
[output.html.fold]
enable = true
level = 1
[output.html]
git-repository-url = "https://github.com/sunface/rust-by-practice"
edit-url-template = "https://github.com/sunface/rust-by-practice/edit/master/en/{path}"
[rust]
edition = "2021"

View File

@ -1,21 +0,0 @@
## this script deploys the static website of course.rs to github pages
## build static website for book
mdbook build
## copy CNAME info to book dir
cp ./assets/CNAME ./book/
cp ./assets/*.html ./book/
cp ./assets/sitemap.xml ./book/
## init git repo
cd book
git init
git config user.name "sunface"
git config user.email "cto@188.com"
git add .
git commit -m 'deploy'
git branch -M gh-pages
git remote add origin https://github.com/sunface/rust-by-practice
## push to github pages
git push -u -f origin gh-pages

1
en/src/.gitignore vendored
View File

@ -1 +0,0 @@
book

View File

@ -1,82 +0,0 @@
# Summary
- [Rust By Practice](why-exercise.md)
- [Variables](variables.md)
- [Basic Types](basic-types/intro.md)
- [Numbers](basic-types/numbers.md)
- [Char, Bool and Unit](basic-types/char-bool-unit.md)
- [Statements and Expressions](basic-types/statements-expressions.md)
- [Functions](basic-types/functions.md)
- [Ownership and Borrowing](ownership/intro.md)
- [Ownership](ownership/ownership.md)
- [Reference and Borrowing](ownership/borrowing.md)
- [Compound Types doing](compound-types/intro.md)
- [string](compound-types/string.md)
- [Array](compound-types/array.md)
- [Slice](compound-types/slice.md)
- [Tuple](compound-types/tuple.md)
- [Struct](compound-types/struct.md)
- [Enum todo](compound-types/enum.md)
- [Flow Control todo](flow-control.md)
- [Pattern Match todo](pattern-match/intro.md)
- [match, if let](pattern-match/match-iflet.md)
- [Option destructing](pattern-match/option.md)
- [Patterns](pattern-match/patterns.md)
- [Method todo](method.md)
- [Generics and Traits todo](generics-traits/intro.md)
- [Generics](generics-traits/generics.md)
- [Traits](generics-traits/traits.md)
- [Trait Object](generics-traits/trait-object.md)
- [Advance Traits](generics-traits/advance-traits.md)
- [Collection Types todo](collections/intro.md)
- [Vector](collections/vector.md)
- [HashMap](collections/hashmap.md)
- [Type Conversion todo](type-conversion.md)
- [Result and panic todo](result-panic/intro.md)
- [panic!](result-panic/panic.md)
- [result and ?](result-panic/result.md)
- [Crate and module todo](crate-module/intro.md)
- [Crate](crate-module/crate.md)
- [Module](crate-module/module.md)
- [use and pub](crate-module/use-pub.md)
- [Comments and Docs todo](comments-docs.md)
- [Formatted output todo](formatted-output.md)
- [Lifetime todo](lifetime/intro.md)
- [basic](lifetime/basic.md)
- [&'static and T: 'static](lifetime/static.md)
- [advance](lifetime/advance.md)
- [Functional programing todo](functional-programing/intro.md)
- [Closure](functional-programing/cloure.md)
- [Iterator](functional-programing/iterator.md)
- [newtype and Sized todo](newtype-sized.md)
- [Smart pointers todo](smart-pointers/intro.md)
- [Box](smart-pointers/box.md)
- [Deref](smart-pointers/deref.md)
- [Drop](smart-pointers/drop.md)
- [Rc and Arc](smart-pointers/rc-arc.md)
- [Cell and RefCell](smart-pointers/cell-refcell.md)
- [Weak and Circle reference todo](weak.md)
- [Self referential todo](self-referential.md)
- [Threads todo](threads/intro.md)
- [Basic using](threads/basic-using.md)
- [Message passing](threads/message-passing.md)
- [Sync](threads/sync.md)
- [Atomic](threads/atomic.md)
- [Send and Sync](threads/send-sync.md)
- [Global variables todo](global-variables.md)
- [Errors todo](errors.md)
- [Unsafe doing](unsafe/intro.md)
- [Inline assembly](unsafe/inline-asm.md)
- [Macro todo](macro.md)
- [Tests todo](tests/intro.md)
- [Write Tests](tests/write-tests.md)
- [Benchmark](tests/benchmark.md)
- [Unit and Integration](tests/unit-integration.md)
- [Assertions](tests/assertions.md)
- [Async/Await todo](async/intro.md)
- [async and await!](async/async-await.md)
- [Future](async/future.md)
- [Pin and Unpin](async/pin-unpin.md)
- [Stream](async/stream.md)
- [Stand Library todo](std/intro.md)

View File

@ -1 +0,0 @@
# async and await!

View File

@ -1 +0,0 @@
# Future

View File

@ -1 +0,0 @@
# Async/Await

View File

@ -1 +0,0 @@
# Pin and Unpin

View File

@ -1 +0,0 @@
# Stream

View File

@ -1,90 +0,0 @@
# Char, Bool and Unit
### Char
🌟
```rust, editable
// make it work
use std::mem::size_of_val;
fn main() {
let c1 = 'a';
assert_eq!(size_of_val(&c1),1);
let c2 = '中';
assert_eq!(size_of_val(&c2),3);
}
```
🌟
```rust, editable
// make it work
fn main() {
let c1 = "中";
print_char(c1);
}
fn print_char(c : char) {
println!("{}", c);
}
```
### Bool
🌟
```rust, editable
// make the println! work
fn main() {
let _f: bool = false;
let t = true;
if !t {
println!("hello, world");
}
}
```
🌟
```rust, editable
// make it work
fn main() {
let f = true;
let t = true && false;
assert_eq!(t, f);
}
```
### Unit type
🌟🌟
```rust,editable
// make it work, don't modify `implicitly_ret_unit` !
fn main() {
let _v: () = ();
let v = (2, 3);
assert_eq!(v, implicitly_ret_unit())
}
fn implicitly_ret_unit() {
println!("I will returen a ()")
}
// don't use this one
fn explicitly_ret_unit() -> () {
println!("I will returen a ()")
}
```
🌟🌟 what's the size of the unit type?
```rust,editable
// modify `4` in assert to make it work
use std::mem::size_of_val;
fn main() {
let unit: () = ();
assert!(size_of_val(&unit) == 4);
}
```

View File

@ -1,43 +0,0 @@
# Functions
🌟🌟🌟
```rust,editable
fn main() {
// don't modify the following two lines!
let (x, y) = (1, 2);
let s = sum(1, 2);
assert_eq!(s, 3);
}
fn sum(x, y: i32) {
x + y;
}
```
🌟🌟
```rust,editable
fn main() {
print();
}
// replace i32 with another type
fn print() -> i32 {
println!("hello,world");
}
```
🌟🌟
```rust,editable
fn main() {
never_return();
}
fn never_return() -> ! {
// implement this function, don't modify fn signatures
}
```

View File

@ -1,5 +0,0 @@
# Basic Types
Learning resources:
- English: [Rust Book 3.2 and 3.3](https://doc.rust-lang.org/book/ch03-02-data-types.html)
- 简体中文: [Rust语言圣经 - 基本类型](https://course.rs/basic/base-type/index.html)

View File

@ -1,165 +0,0 @@
# Numbers
### Integer
🌟
> Tips: If we don't explicitly give one type to a varible, then the compiler will infer one for us
```rust,editable
// remove something to make it work
fn main() {
let x: i32 = 5;
let mut y: u32 = 5;
y = x;
let z = 10; // type of z ?
}
```
🌟
```rust,editable
// fill the blank
fn main() {
let v: u16 = 38_u8 as __;
}
```
🌟🌟🌟
> Tips: If we don't explicitly give one type to a varible, then the compiler will infer one for us
```rust,editable
// modify `assert_eq!` to make it work
fn main() {
let x = 5;
assert_eq!("u32".to_string(), type_of(&x));
}
// get the type of given variable, return a string representation of the type , e.g "i8", "u8", "i32", "u32"
fn type_of<T>(_: &T) -> String {
format!("{}", std::any::type_name::<T>())
}
```
🌟🌟
```rust,editable
// fill the blanks to make it work
fn main() {
assert_eq!(i8::MAX, __);
assert_eq!(u8::MAX, __);
}
```
🌟🌟
```rust,editable
// fix errors and panics to make it work
fn main() {
let v1 = 251_u8 + 8;
let v2 = i8::checked_add(251, 8).unwrap();
println!("{},{}",v1,v2);
}
```
🌟🌟🌟
```rust,editable
// modify `assert!` to make it work
fn main() {
let v = 1_024 + 0xff + 0o77 + 0b1111_1111;
assert!(v == 1579);
}
```
### Floating-Point
🌟
```rust,editable
// replace ? with your answer
fn main() {
let x = 1_000.000_1; // ?
let y: f32 = 0.12; // f32
let z = 0.01_f64; // f64
}
```
🌟🌟🌟 use two ways to make it work
> Tips: 1. abs 2. f32
```rust,editable
fn main() {
assert!(0.1+0.2==0.3);
}
```
### Range
🌟🌟 two targets: 1. modify `assert!` to make it work 2. make `println!` output: 97 - 122
> Tips: use `as u8` to convert a char to u8
```rust,editable
fn main() {
let mut sum = 0;
for i in -3..2 {
sum += i
}
assert!(sum == -3);
for c in 'a'..='Z' {
println!("{}",c);
}
}
```
🌟🌟
```rust,editable
// fill the blanks
use std::ops::{Range, RangeInclusive};
fn main() {
assert_eq!((1..__), Range{ start: 1, end: 5 });
assert_eq!((1..__), RangeInclusive::new(1, 5));
}
```
### Computations
🌟
```rust,editable
// fill the blanks
fn main() {
// Integer addition
assert!(1u32 + 2 == __);
// Integer subtraction
assert!(1i32 - 2 == __);
assert!(1u8 - 2 == -1); // change u8 to another type to make it work
assert!(3 * 50 == __);
assert!(9.6 / 3.2 == 3.0); // error ! make it work
assert!(24 % 5 == __);
// Short-circuiting boolean logic
assert!(true && false == __);
assert!(true || false == __);
assert!(!true == __);
// Bitwise operations
println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101);
println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101);
println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101);
println!("1 << 5 is {}", 1u32 << 5);
println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2);
}
```

View File

@ -1,59 +0,0 @@
# Statements and Expressions
### Examples
```rust,editable
fn main() {
let x = 5u32;
let y = {
let x_squared = x * x;
let x_cube = x_squared * x;
// This expression will be assigned to `y`
x_cube + x_squared + x
};
let z = {
// The semicolon suppresses this expression and `()` is assigned to `z`
2 * x;
};
println!("x is {:?}", x);
println!("y is {:?}", y);
println!("z is {:?}", z);
}
```
### Exercises
🌟🌟
```rust,editable
// make it work with two ways: both modify the inner {}
fn main() {
let v = {
let mut x = 1;
x += 2
};
assert_eq!(v, 3);
}
```
🌟
```rust,editable
fn main() {
let v = (let x = 3);
assert!(v == 3);
}
```
🌟
```rust,editable
fn main() {}
fn sum(x: i32, y: i32) -> i32 {
x + y;
}
```

View File

@ -1 +0,0 @@
# Circle reference and Self referential

View File

@ -1 +0,0 @@
# HashMap

View File

@ -1 +0,0 @@
# Collection Types

View File

@ -1 +0,0 @@
# Vector

View File

@ -1 +0,0 @@
# Comments and Docs

View File

@ -1,89 +0,0 @@
# Array
The type of array is `[T; Lengh]`, as you can see, array's lengh is part of their type signature. So their length must be known at compile time.
For example, you cant initialized an array as below:
```rust
fn init_arr(n: i32) {
let arr = [1; n];
}
```
This will cause an error, because the compile have no idea of the exact size of the array in compile time.
🌟
```rust,editable
fn main() {
// fill the blank with proper array type
let arr: __ = [1, 2, 3, 4, 5];
// modify below to make it work
assert!(arr.len() == 4);
}
```
🌟🌟
```rust,editable
fn main() {
// we can ignore parts of the array type or even the whole type, let the compiler infer it for us
let arr0 = [1, 2, 3];
let arr: [_; 3] = ['a', 'b', 'c'];
// fill the blank
// Arrays are stack allocated, `std::mem::size_of_val` return the bytes which array occupies
// A char takes 4 byte in Rust: Unicode char
assert!(std::mem::size_of_val(&arr) == __);
}
```
🌟 All elements in an array can be initialized to the same value at once.
```rust,editable
fn main() {
// fill the blank
let list: [i32; 100] = __ ;
assert!(list[0] == 1);
assert!(list.len() == 100);
}
```
🌟 All elements in an array must be of the same type
```rust,editable
fn main() {
// fix the error
let _arr = [1, 2, '3'];
}
```
🌟 Indexing starts at 0.
```rust,editable
fn main() {
let arr = ['a', 'b', 'c'];
let ele = arr[1]; // only modify this line to make the code work!
assert!(ele == 'a');
}
```
🌟 Out of bounds indexing causes `panic`.
```rust,editable
// fix the error
fn main() {
let names = [String::from("Sunfei"), "Sunface".to_string()];
// `get` returns an Option<T>, it's safe to use
let name0 = names.get(0).unwrap();
// but indexing is not safe
let _name1 = &names[2];
}
```

View File

@ -1 +0,0 @@
# enum

View File

@ -1,6 +0,0 @@
# Compound Types
Learning resources:
- English: [Rust Book 4.3, 5.1, 6.1, 8.2](https://doc.rust-lang.org/book/ch04-03-slices.html)
- 简体中文: [Rust语言圣经 - 复合类型](https://course.rs/basic/compound-type/intro.html)

View File

@ -1,88 +0,0 @@
# Slice
Slices are similar to arrays, but their length is not known at compile time, so you can't use slice directly.
🌟🌟 Here, both `[i32]` and `str` are slice types, but directly using it will cause errors. You have to use the reference of the slice instead: `&[i32]`, `&str`.
```rust,editable
// fix the errors, DON'T add new lines!
fn main() {
let arr = [1, 2, 3];
let s1: [i32] = arr[0..2];
let s2: str = "hello, world" as str;
}
```
A slice reference is a two-word object, for simplicity reasons, from now on we will use slice instead of `slice reference`. The first word is a pointer to the data, and the second word is the length of the slice. The word size is the same as usize, determined by the processor architecture eg 64 bits on an x86-64. Slices can be used to borrow a section of an array, and have the type signature `&[T]`.
🌟🌟🌟
```rust,editable
fn main() {
let arr: [char; 3] = ['中', '国', '人'];
let slice = &arr[..2];
// modify '6' to make it work
// TIPS: slice( reference ) IS NOT an array, if it is an array, then `assert!` will passed: each of the two UTF-8 chars '中' and '国' occupies 3 bytes, 2 * 3 = 6
assert!(std::mem::size_of_val(&slice) == 6);
}
```
🌟🌟
```rust,editable
fn main() {
let arr: [i32; 5] = [1, 2, 3, 4, 5];
// fill the blanks to make the code work
let slice: __ = __;
assert_eq!(slice, &[2, 3, 4]);
}
```
### string slices
🌟
```rust,editable
fn main() {
let s = String::from("hello");
let slice1 = &s[0..2];
// fill the blank to make the code work
let slice2 = &s[__];
assert_eq!(slice1, slice2);
}
```
🌟
```rust,editable
fn main() {
let s = "你好,世界";
// modify this line to make the code work
let slice = &s[0..2];
assert!(slice == "你");
}
```
🌟🌟 `&String` can be implicitly converted into `&str`.
```rust,editable
// fix errors
fn main() {
let mut s = String::from("hello world");
// here, &s is `&String` type, but `first_word` need a `&str` type.
// it works because `&String` implicitly be converted to `&str, If you want know more ,this is called `Deref`
let word = first_word(&s);
s.clear(); // error!
println!("the first word is: {}", word);
}
fn first_word(s: &str) -> &str {
&s[..1]
}
```

View File

@ -1,255 +0,0 @@
# string
The type of string literal `"hello, world"` is `&str`, e.g `let s: &str = "hello, world"`.
### str and &str
🌟 We can't use `str` type in normal ways, but we can use `&str`
```rust,editable
// fix error without adding new line
fn main() {
let s: str = "hello, world";
}
```
🌟🌟🌟 We can only use `str` by boxed it, `&` can be used to convert `Box<str>` to `&str`
```rust,editable
// fix error with at least two ways
fn main() {
let s: Box<str> = "hello, world".into();
greetings(s)
}
fn greetings(s: &str) {
println!("{}",s)
}
```
### String
`String` type is defined in std and stored as a vector of bytes (Vec<u8>), but guaranteed to always be a valid UTF-8 sequence. String is heap allocated, growable and not null terminated.
🌟
```rust,editable
// fill the blank
fn main() {
let mut s = __;
s.push_str("hello, world");
s.push('!');
assert_eq!(s, "hello, world!");
}
```
🌟🌟🌟
```rust,editable
// fix all errors without adding newline
fn main() {
let s = String::from("hello");
s.push(',');`
s.push(" world");
s += "!".to_string();
println!("{}", s)
}
```
🌟🌟 `replace` can be used to replace substring
```rust,editable
// fill the blank
fn main() {
let s = String::from("I like dogs");
// Allocate new memory and store the modified string there
let s1 = s.__("dogs", "cats");
assert_eq!(s1, "I like cats")
}
```
More `String` methods can be found under [String](https://doc.rust-lang.org/std/string/struct.String.html) module.
🌟🌟 You can only concat a `String` with `&str`, and `String`'s ownership can be moved to another variable
```rust,editable
// fix errors
fn main() {
let s1 = String::from("hello,");
let s2 = String::from("world!");
let s3 = s1 + s2;
assert_eq!(s3,"hello,world!");
println!("{}",s1);
}
```
### &str and String
Opsite to the seldom using of `str`, `&str` and `String` are used everywhere!
🌟🌟 `&str` can be converted to `String` in two ways
```rust,editable
// fix error with at lest two ways
fn main() {
let s = "hello, world";
greetings(s)
}
fn greetings(s: String) {
println!("{}",s)
}
```
🌟🌟 We can use `String::from` or `to_string` to convert a `&str` to `String`
```rust,editable
// use two ways to fix error and without adding new line
fn main() {
let s = "hello, world".to_string();
let s1: &str = s;
}
```
### string escapes
🌟
```rust,editable
fn main() {
// You can use escapes to write bytes by their hexadecimal values
// fill the blank below to show "I'm writing Rust"
let byte_escape = "I'm writing Ru\x73__!";
println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);
// ...or Unicode code points.
let unicode_codepoint = "\u{211D}";
let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";
println!("Unicode character {} (U+211D) is called {}",
unicode_codepoint, character_name );
let long_string = "String literals
can span multiple lines.
The linebreak and indentation here \
can be escaped too!";
println!("{}", long_string);
}
```
🌟🌟🌟 Sometimes there are just too many characters that need to be escaped or it's just much more convenient to write a string out as-is. This is where raw string literals come into play.
```rust,editable
fn main() {
let raw_str = r"Escapes don't work here: \x3F \u{211D}";
// modify below line to make it work
assert_eq!(raw_str, "Escapes don't work here: ? ");
// If you need quotes in a raw string, add a pair of #s
let quotes = r#"And then I said: "There is no escape!""#;
println!("{}", quotes);
// If you need "# in your string, just use more #s in the delimiter.
// You can use up to 65535 #s.
let delimiter = r###"A string with "# in it. And even "##!"###;
println!("{}", delimiter);
// fill the blank
let long_delimiter = __;
assert_eq!(long_delimiter, "Hello, \"##\"")
}
```
### byte string
Want a string that's not UTF-8? (Remember, str and String must be valid UTF-8). Or maybe you want an array of bytes that's mostly text? Byte strings to the rescue!
**Example**:
```rust,editable
use std::str;
fn main() {
// Note that this is not actually a `&str`
let bytestring: &[u8; 21] = b"this is a byte string";
// Byte arrays don't have the `Display` trait, so printing them is a bit limited
println!("A byte string: {:?}", bytestring);
// Byte strings can have byte escapes...
let escaped = b"\x52\x75\x73\x74 as bytes";
// ...but no unicode escapes
// let escaped = b"\u{211D} is not allowed";
println!("Some escaped bytes: {:?}", escaped);
// Raw byte strings work just like raw strings
let raw_bytestring = br"\u{211D} is not escaped here";
println!("{:?}", raw_bytestring);
// Converting a byte array to `str` can fail
if let Ok(my_str) = str::from_utf8(raw_bytestring) {
println!("And the same as text: '{}'", my_str);
}
let _quotes = br#"You can also use "fancier" formatting, \
like with normal raw strings"#;
// Byte strings don't have to be UTF-8
let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82\xbb"; // "ようこそ" in SHIFT-JIS
// But then they can't always be converted to `str`
match str::from_utf8(shift_jis) {
Ok(my_str) => println!("Conversion successful: '{}'", my_str),
Err(e) => println!("Conversion failed: {:?}", e),
};
}
```
A more detailed listing of the ways to write string literals and escape characters is given in the ['Tokens' chapter](https://doc.rust-lang.org/reference/tokens.html) of the Rust Reference.
### string index
🌟🌟 You can't use index to access a char in a string, but you can use slice `&s1[start..end]`.
```rust,editable
fn main() {
let s1 = String::from("hi,中国");
let h = s1[0]; //modify this line to fix the error, tips: `h` only takes 1 byte in UTF8 format
assert_eq!(h, "h");
let h1 = &s1[3..5];//modify this line to fix the error, tips: `` takes 3 bytes in UTF8 format
assert_eq!(h1, "中");
}
```
### operate on UTF8 string
🌟
```rust,editable
fn main() {
// fill the blank to print each char in "你好,世界"
for c in "你好,世界".__ {
println!("{}", c)
}
}
```
#### utf8_slice
You can use [utf8_slice](https://docs.rs/utf8_slice/1.0.0/utf8_slice/fn.slice.html) to slice UTF8 string, it can index chars instead of bytes.
**Example**
```rust
use utf_slice;
fn main() {
let s = "The 🚀 goes to the 🌑!";
let rocket = utf8_slice::slice(s, 4, 5);
// Will equal "🚀"
}
```

View File

@ -1,216 +0,0 @@
# Struct
### There types of structs
🌟 We must specify concrete values for each of the fields in struct.
```rust,editable
// fix the error
struct Person {
name: String,
age: u8,
hobby: String
}
fn main() {
let age = 30;
let p = Person {
name: String::from("sunface"),
age,
};
}
```
🌟 Unit struct don't have any fields. It can be useful when you need to implement a trait on some type but dont have any data that you want to store in the type itself.
```rust,editable
struct Unit;
trait SomeTrait {
// ...Some behavours defines here
}
// We don't care the the fields are in Unit, but we care its behaviors.
// So we use a struct with no fields and implement some behaviors for it
impl SomeTrait for Unit { }
fn main() {
let u = Unit;
do_something_with_unit(u);
}
// fill the blank to make the code work
fn do_something_with_unit(u: __) { }
```
🌟🌟🌟 Tuple struct looks similar to tuples, it has added meaning the struct name provides but has no named fields. It's useful when you want give the whole tuple a name, but don't care the fields's names.
```rust,editable
// fix the error and fill the blanks
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let v = Point(__, __, __);
check_color(v);
}
fn check_color(p: Color) {
let (x, _, _) = p;
assert_eq!(x, 0);
assert_eq!(p.1, 127);
assert_eq!(__, 255);
}
```
### Operate on structs
🌟 You can make a whole struct mutable when instantiate it, but Rust doesn't allow us to mark only certain fields as mutable.
```rust,editable
// fill the blank and fix the error without adding/removing new line
struct Person {
name: String,
age: u8,
}
fn main() {
let age = 18;
let p = Person {
name: String::from("sunface"),
age,
};
// how can you believe sunface is only 18?
p.age = 30
// fill the lank
__ = String::from("sunfei");
}
```
🌟 Using *field init shorthand syntax* to reduct repetitions.
```rust,editable
// fill the blank
struct Person {
name: String,
age: u8,
}
fn main() {}
fn build_person(name: String, age: u8) -> Person {
Person {
age,
__
}
}
```
🌟 You can create instance from other instance with *struct update syntax*
```rust,editable
// fill the blank to make the code work
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
fn main() {
let u1 = User {
email: String::from("someone@example.com"),
username: String::from("sunface"),
active: true,
sign_in_count: 1,
};
let u2 = set_email(u1);
}
fn set_email(u: User) -> User {
User {
email: String::from("contact@im.dev"),
__
}
}
```
### Print the structs
🌟🌟 We can use `#[derive(Debug)]` to [make a struct prinable](https://doc.rust-lang.org/book/ch05-02-example-structs.html?highlight=%23%5Bderive(Debug)%5D#adding-useful-functionality-with-derived-traits).
```rust,editable
// fill the blanks to make the code work
#[__]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let scale = 2;
let rect1 = Rectangle {
width: dbg!(30 * scale), // print debug info to stderr and assign the value of `30 * scale` to `width`
height: 50,
};
dbg!(&rect1); // print debug info to stderr
println!(__, rect1); // print debug info to stdout
}
```
### Partial move
Within the destructuring of a single variable, both by-move and by-reference pattern bindings can be used at the same time. Doing this will result in a partial move of the variable, which means that parts of the variable will be moved while other parts stay. In such a case, the parent variable cannot be used afterwards as a whole, however the parts that are only referenced (and not moved) can still be used.
#### Example
```rust,editable
fn main() {
#[derive(Debug)]
struct Person {
name: String,
age: Box<u8>,
}
let person = Person {
name: String::from("Alice"),
age: Box::new(20),
};
// `name` is moved out of person, but `age` is referenced
let Person { name, ref age } = person;
println!("The person's age is {}", age);
println!("The person's name is {}", name);
// Error! borrow of partially moved value: `person` partial move occurs
//println!("The person struct is {:?}", person);
// `person` cannot be used but `person.age` can be used as it is not moved
println!("The person's age from person struct is {}", person.age);
}
```
#### Exercises
🌟🌟
```rust,editable
// fix errors to make it work
#[derive(Debug)]
struct File {
name: String,
data: String,
}
fn main() {
let f = File {
name: String::from("readme.md"),
data: "Rust By Practice".to_string()
};
let _name = f.name;
println!("{}, {}, {:?}",f.name, f.data, f);
}
```

View File

@ -1,77 +0,0 @@
# Tuple
🌟 Elements in a tuple can have different types. Tuple's type signature is `(T1, T2, ...)`, where `T1`, `T2` are the types of tuple's members.
```rust,editable
fn main() {
let _t0: (u8,i16) = (0, -1);
// Tuples can be tuple's members
let _t1: (u8, (i16, u32)) = (0, (-1, 1));
// fill the blanks to make the code work
let t: (u8, __, i64, __, __) = (1u8, 2u16, 3i64, "hello", String::from(", world"));
}
```
🌟 Members can be extracted from the tuple using indexing.
```rust,editable
// make it works
fn main() {
let t = ("i", "am", "sunface");
assert_eq!(t.1, "sunface");
}
```
🌟 Long tuples cannot be printed
```rust,editable
// fix the error
fn main() {
let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
println!("too long tuple: {:?}", too_long_tuple);
}
```
🌟 Destructuring tuple with pattern.
```rust,editable
fn main() {
let tup = (1, 6.4, "hello");
// fill the blank to make the code work
let __ = tup;
assert_eq!(x, 1);
assert_eq!(y, "hello");
assert_eq!(z, 6.4);
}
```
🌟🌟 Destructure assignments.
```rust,editable
fn main() {
let (x, y, z);
// fill the blank
__ = (1, 2, 3);
assert_eq!(x, 3);
assert_eq!(y, 1);
assert_eq!(z, 2);
}
```
🌟🌟 Tuples can be used as function arguments and return values
```rust,editable
fn main() {
// fill the blank, need a few computations here.
let (x, y) = sum_multiply(__);
assert_eq!(x, 5);
assert_eq!(y, 6);
}
fn sum_multiply(nums: (i32, i32)) -> (i32, i32) {
(nums.0 + nums.1, nums.0 * nums.1)
}
```

View File

@ -1 +0,0 @@
# Crate

View File

@ -1 +0,0 @@
# Crate and module

View File

@ -1 +0,0 @@
# Module

View File

@ -1 +0,0 @@
# use and pub

View File

@ -1 +0,0 @@
# Errors

View File

@ -1 +0,0 @@
# Flow Control

View File

@ -1 +0,0 @@
# Formatted output

View File

@ -1,51 +0,0 @@
# Closure
下面代码是Rust圣经课程中[闭包](http://course.rs/advance/functional-programing/closure.html#结构体中的闭包)章节的课内练习题答案:
```rust
struct Cacher<T,E>
where
T: Fn(E) -> E,
E: Copy
{
query: T,
value: Option<E>,
}
impl<T,E> Cacher<T,E>
where
T: Fn(E) -> E,
E: Copy
{
fn new(query: T) -> Cacher<T,E> {
Cacher {
query,
value: None,
}
}
fn value(&mut self, arg: E) -> E {
match self.value {
Some(v) => v,
None => {
let v = (self.query)(arg);
self.value = Some(v);
v
}
}
}
}
fn main() {
}
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a| a);
let v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 1);
}
```

View File

@ -1 +0,0 @@
# Functional programing

View File

@ -1,19 +0,0 @@
# Iterator
```rust,editable
// (all the type annotations are superfluous)
// A reference to a string allocated in read only memory
let pangram: &'static str = "the quick brown fox jumps over the lazy dog";
println!("Pangram: {}", pangram);
// Iterate over words in reverse, no new string is allocated
println!("Words in reverse");
for word in pangram.split_whitespace().rev() {
println!("> {}", word);
}
// Copy chars into a vector, sort and remove duplicates
let mut chars: Vec<char> = pangram.chars().collect();
chars.sort();
chars.dedup();
```

View File

@ -1,51 +0,0 @@
# Closure
下面代码是Rust圣经课程中[闭包](http://course.rs/advance/functional-programing/closure.html#结构体中的闭包)章节的课内练习题答案:
```rust
struct Cacher<T,E>
where
T: Fn(E) -> E,
E: Copy
{
query: T,
value: Option<E>,
}
impl<T,E> Cacher<T,E>
where
T: Fn(E) -> E,
E: Copy
{
fn new(query: T) -> Cacher<T,E> {
Cacher {
query,
value: None,
}
}
fn value(&mut self, arg: E) -> E {
match self.value {
Some(v) => v,
None => {
let v = (self.query)(arg);
self.value = Some(v);
v
}
}
}
}
fn main() {
}
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a| a);
let v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 1);
}
```

View File

@ -1 +0,0 @@
# Functional Programming

View File

@ -1 +0,0 @@
# Advance Traits

View File

@ -1 +0,0 @@
# Generics

View File

@ -1 +0,0 @@
# Generics and Traits

View File

@ -1 +0,0 @@
# Trait Object

View File

@ -1 +0,0 @@
# Traits

View File

@ -1 +0,0 @@
# Global variables

View File

@ -1 +0,0 @@
# advance

View File

@ -1,26 +0,0 @@
## 生命周期消除
```rust
fn print(s: &str); // elided
fn print<'a>(s: &'a str); // expanded
fn debug(lvl: usize, s: &str); // elided
fn debug<'a>(lvl: usize, s: &'a str); // expanded
fn substr(s: &str, until: usize) -> &str; // elided
fn substr<'a>(s: &'a str, until: usize) -> &'a str; // expanded
fn get_str() -> &str; // ILLEGAL
fn frob(s: &str, t: &str) -> &str; // ILLEGAL
fn get_mut(&mut self) -> &mut T; // elided
fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded
fn args<T: ToCStr>(&mut self, args: &[T]) -> &mut Command // elided
fn args<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // expanded
fn new(buf: &mut [u8]) -> BufWriter; // elided
fn new(buf: &mut [u8]) -> BufWriter<'_>; // elided (with `rust_2018_idioms`)
fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded
```

View File

@ -1 +0,0 @@
# Lifetime

View File

@ -1,49 +0,0 @@
# &'static and T: 'static
```rust,editable
use std::fmt::Display;
fn main() {
let mut string = "First".to_owned();
string.push_str(string.to_uppercase().as_str());
print_a(&string);
print_b(&string);
print_c(&string); // Compilation error
print_d(&string); // Compilation error
print_e(&string);
print_f(&string);
print_g(&string); // Compilation error
}
fn print_a<T: Display + 'static>(t: &T) {
println!("{}", t);
}
fn print_b<T>(t: &T)
where
T: Display + 'static,
{
println!("{}", t);
}
fn print_c(t: &'static dyn Display) {
println!("{}", t)
}
fn print_d(t: &'static impl Display) {
println!("{}", t)
}
fn print_e(t: &(dyn Display + 'static)) {
println!("{}", t)
}
fn print_f(t: &(impl Display + 'static)) {
println!("{}", t)
}
fn print_g(t: &'static String) {
println!("{}", t);
}
```

View File

@ -1 +0,0 @@
# macro

View File

@ -1 +0,0 @@
# Method

View File

@ -1 +0,0 @@
# newtype and Sized

View File

@ -1,166 +0,0 @@
# Reference and Borrowing
### Reference
🌟
```rust,editable
fn main() {
let x = 5;
// fill the blank
let p = __;
println!("the memory address of x is {:p}", p); // output: 0x16fa3ac84
}
```
🌟
```rust,editable
fn main() {
let x = 5;
let y = &x;
// modify this line only
assert_eq!(5, y);
}
```
🌟
```rust,editable
// fix error
fn main() {
let mut s = String::from("hello, ");
borrow_object(s)
}
fn borrow_object(s: &String) {}
```
🌟
```rust,editable
// fix error
fn main() {
let mut s = String::from("hello, ");
borrow_object(&s)
}
fn borrow_object(s: &mut String) {}
```
🌟🌟
```rust,editable
fn main() {
let mut s = String::from("hello, ");
// fill the blank to make it work
let p = __;
p.push_str("world");
}
```
#### ref
`ref` can be used to take references to a value, similar to `&`.
🌟🌟🌟
```rust,editable
fn main() {
let c = '中';
let r1 = &c;
// fill the blankdont change other code
let __ r2 = c;
assert_eq!(*r1, *r2);
// check the equality of the two address strings
assert_eq!(get_addr(r1),get_addr(r2));
}
// get memory address string
fn get_addr(r: &char) -> String {
format!("{:p}", r)
}
```
### Borrowing rules
🌟
```rust,editable
// remove something to make it work
// don't remove a whole line !
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
}
```
#### Mutablity
🌟 Error: Borrow a immutable object as mutable
```rust,editable
fn main() {
//fix error by modifying this line
let s = String::from("hello, ");
borrow_object(&mut s)
}
fn borrow_object(s: &mut String) {}
```
🌟🌟 Ok: Borrow a mutable object as immutable
```rust,editable
// this code has no errors!
fn main() {
let mut s = String::from("hello, ");
borrow_object(&s);
s.push_str("world");
}
fn borrow_object(s: &String) {}
```
### NLL
🌟🌟
```rust,editable
// comment one line to make it work
fn main() {
let mut s = String::from("hello, ");
let r1 = &mut s;
r1.push_str("world");
let r2 = &mut s;
r2.push_str("!");
println!("{}",r1);
}
```
🌟🌟🌟
```rust,editable
fn main() {
let mut s = String::from("hello, ");
let r1 = &mut s;
let r2 = &mut s;
// add one line below to make a compiler error: cannot borrow `s` as mutable more than once at a time
// you can't use r1 and r2 at the same time
}
```

View File

@ -1,5 +0,0 @@
# Ownership and Borrowing
Learning resources:
- English: [Rust Book 4.1-4.4](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html)
- 简体中文: [Rust语言圣经 - 所有权与借用(https://course.rs/basic/ownership/index.html)

View File

@ -1,163 +0,0 @@
# Ownership
🌟🌟
```rust,editable
fn main() {
// modify this line only! use as many approaches as you can
let x = String::from("hello, world");
let y = x;
println!("{},{}",x,y);
}
```
🌟🌟
```rust,editable
// Don't modify code in main!
fn main() {
let s1 = String::from("hello, world");
let s2 = take_ownership(s1);
println!("{}", s2);
}
// Only modify the code below!
fn take_ownership(s: String) {
println!("{}", s);
}
```
🌟🌟
```rust,editable
fn main() {
let s = give_ownership();
println!("{}", s);
}
// Only modify the code below!
fn give_ownership() -> String {
let s = String::from("hello, world");
// convert String to Vec
let _s = s.into_bytes();
s
}
```
🌟🌟
```rust,editable
// use clone to fix it
fn main() {
let s = String::from("hello, world");
print_str(s);
println!("{}", s);
}
fn print_str(s: String) {
println!("{}",s)
}
```
🌟🌟
```rust, editable
// don't use clone ,use copy instead
fn main() {
let x = (1, 2, (), "hello");
let y = x.clone();
println!("{:?}, {:?}", x, y);
}
```
#### Mutability
Mutability can be changed when ownership is transferred.
🌟
```rust,editable
fn main() {
let s = String::from("hello, ");
// modify this line only !
let s1 = s;
s1.push_str("world")
}
```
🌟🌟🌟
```rust,editable
fn main() {
let x = Box::new(5);
let ... // implement this line, dont change other lines!
*y = 4;
assert_eq!(*x, 5);
}
```
### Partial move
Within the destructuring of a single variable, both by-move and by-reference pattern bindings can be used at the same time. Doing this will result in a partial move of the variable, which means that parts of the variable will be moved while other parts stay. In such a case, the parent variable cannot be used afterwards as a whole, however the parts that are only referenced (and not moved) can still be used.
#### Example
```rust,editable
fn main() {
#[derive(Debug)]
struct Person {
name: String,
age: Box<u8>,
}
let person = Person {
name: String::from("Alice"),
age: Box::new(20),
};
// `name` is moved out of person, but `age` is referenced
let Person { name, ref age } = person;
println!("The person's age is {}", age);
println!("The person's name is {}", name);
// Error! borrow of partially moved value: `person` partial move occurs
//println!("The person struct is {:?}", person);
// `person` cannot be used but `person.age` can be used as it is not moved
println!("The person's age from person struct is {}", person.age);
}
```
#### Exercises
🌟
```rust,editable
fn main() {
let t = (String::from("hello"), String::from("world"));
let _s = t.0;
// modify this line only, don't use `_s`
println!("{:?}", t);
}
```
🌟🌟
```rust,editable
fn main() {
let t = (String::from("hello"), String::from("world"));
// fill the blanks
let (__, __) = t;
println!("{:?}, {:?}, {:?}", s1, s2, t);
}
```

View File

@ -1 +0,0 @@
# Pattern Match

View File

@ -1 +0,0 @@
# match, if let

View File

@ -1 +0,0 @@
# Option destructing

View File

@ -1 +0,0 @@
# Patterns

View File

@ -1 +0,0 @@
# Result and panic

View File

@ -1 +0,0 @@
# panic!

View File

@ -1 +0,0 @@
# result and ?

View File

@ -1 +0,0 @@
# Self referential

View File

@ -1 +0,0 @@
# Box

View File

@ -1 +0,0 @@
# Cell and RefCell

View File

@ -1 +0,0 @@
# Deref

View File

@ -1 +0,0 @@
# Drop

View File

@ -1 +0,0 @@
# Smart pointers

View File

@ -1 +0,0 @@
# Rc and Arc

View File

@ -1 +0,0 @@
# Stand Library todo

View File

@ -1 +0,0 @@
# Assertions

View File

@ -1,3 +0,0 @@
# Benchmark
https://doc.rust-lang.org/unstable-book/library-features/test.html

View File

@ -1 +0,0 @@
# Tests

View File

@ -1 +0,0 @@
# Unit and Integration

View File

@ -1 +0,0 @@
# Write Tests

View File

@ -1 +0,0 @@
# Atomic

View File

@ -1 +0,0 @@
# Basic using

View File

@ -1 +0,0 @@
# Threads

View File

@ -1 +0,0 @@
# Message passing

View File

@ -1 +0,0 @@
# Send and Sync

View File

@ -1 +0,0 @@
# Sync

View File

@ -1 +0,0 @@
# Type Conversion

View File

@ -1,441 +0,0 @@
# Inline assembly
Rust provides support for inline assembly via the `asm!` macro.
It can be used to embed handwritten assembly in the assembly output generated by the compiler.
Generally this should not be necessary, but might be where the required performance or timing
cannot be otherwise achieved. Accessing low level hardware primitives, e.g. in kernel code, may also demand this functionality.
> **Note**: the examples here are given in x86/x86-64 assembly, but other architectures are also supported.
Inline assembly is currently supported on the following architectures:
- x86 and x86-64
- ARM
- AArch64
- RISC-V
## Basic usage
Let us start with the simplest possible example:
```rust
use std::arch::asm;
unsafe {
asm!("nop");
}
```
This will insert a NOP (no operation) instruction into the assembly generated by the compiler.
Note that all `asm!` invocations have to be inside an `unsafe` block, as they could insert
arbitrary instructions and break various invariants. The instructions to be inserted are listed
in the first argument of the `asm!` macro as a string literal.
## Inputs and outputs
Now inserting an instruction that does nothing is rather boring. Let us do something that
actually acts on data:
```rust
use std::arch::asm;
let x: u64;
unsafe {
asm!("mov {}, 5", out(reg) x);
}
assert_eq!(x, 5);
```
This will write the value `5` into the `u64` variable `x`.
You can see that the string literal we use to specify instructions is actually a template string.
It is governed by the same rules as Rust [format strings][format-syntax].
The arguments that are inserted into the template however look a bit different than you may
be familiar with. First we need to specify if the variable is an input or an output of the
inline assembly. In this case it is an output. We declared this by writing `out`.
We also need to specify in what kind of register the assembly expects the variable.
In this case we put it in an arbitrary general purpose register by specifying `reg`.
The compiler will choose an appropriate register to insert into
the template and will read the variable from there after the inline assembly finishes executing.
[format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax
Let us see another example that also uses an input:
```rust
use std::arch::asm;
let i: u64 = 3;
let o: u64;
unsafe {
asm!(
"mov {0}, {1}",
"add {0}, 5",
out(reg) o,
in(reg) i,
);
}
assert_eq!(o, 8);
```
This will add `5` to the input in variable `i` and write the result to variable `o`.
The particular way this assembly does this is first copying the value from `i` to the output,
and then adding `5` to it.
The example shows a few things:
First, we can see that `asm!` allows multiple template string arguments; each
one is treated as a separate line of assembly code, as if they were all joined
together with newlines between them. This makes it easy to format assembly
code.
Second, we can see that inputs are declared by writing `in` instead of `out`.
Third, we can see that we can specify an argument number, or name as in any format string.
For inline assembly templates this is particularly useful as arguments are often used more than once.
For more complex inline assembly using this facility is generally recommended, as it improves
readability, and allows reordering instructions without changing the argument order.
We can further refine the above example to avoid the `mov` instruction:
```rust
use std::arch::asm;
let mut x: u64 = 3;
unsafe {
asm!("add {0}, 5", inout(reg) x);
}
assert_eq!(x, 8);
```
We can see that `inout` is used to specify an argument that is both input and output.
This is different from specifying an input and output separately in that it is guaranteed to assign both to the same register.
It is also possible to specify different variables for the input and output parts of an `inout` operand:
```rust
use std::arch::asm;
let x: u64 = 3;
let y: u64;
unsafe {
asm!("add {0}, 5", inout(reg) x => y);
}
assert_eq!(y, 8);
```
## Late output operands
The Rust compiler is conservative with its allocation of operands. It is assumed that an `out`
can be written at any time, and can therefore not share its location with any other argument.
However, to guarantee optimal performance it is important to use as few registers as possible,
so they won't have to be saved and reloaded around the inline assembly block.
To achieve this Rust provides a `lateout` specifier. This can be used on any output that is
written only after all inputs have been consumed.
There is also a `inlateout` variant of this specifier.
Here is an example where `inlateout` *cannot* be used:
```rust
use std::arch::asm;
let mut a: u64 = 4;
let b: u64 = 4;
let c: u64 = 4;
unsafe {
asm!(
"add {0}, {1}",
"add {0}, {2}",
inout(reg) a,
in(reg) b,
in(reg) c,
);
}
assert_eq!(a, 12);
```
Here the compiler is free to allocate the same register for inputs `b` and `c` since it knows they have the same value. However it must allocate a separate register for `a` since it uses `inout` and not `inlateout`. If `inlateout` was used, then `a` and `c` could be allocated to the same register, in which case the first instruction to overwrite the value of `c` and cause the assembly code to produce the wrong result.
However the following example can use `inlateout` since the output is only modified after all input registers have been read:
```rust
use std::arch::asm;
let mut a: u64 = 4;
let b: u64 = 4;
unsafe {
asm!("add {0}, {1}", inlateout(reg) a, in(reg) b);
}
assert_eq!(a, 8);
```
As you can see, this assembly fragment will still work correctly if `a` and `b` are assigned to the same register.
## Explicit register operands
Some instructions require that the operands be in a specific register.
Therefore, Rust inline assembly provides some more specific constraint specifiers.
While `reg` is generally available on any architecture, explicit registers are highly architecture specific. E.g. for x86 the general purpose registers `eax`, `ebx`, `ecx`, `edx`, `ebp`, `esi`, and `edi` among others can be addressed by their name.
```rust,no_run
use std::arch::asm;
let cmd = 0xd1;
unsafe {
asm!("out 0x64, eax", in("eax") cmd);
}
```
In this example we call the `out` instruction to output the content of the `cmd` variable to port `0x64`. Since the `out` instruction only accepts `eax` (and its sub registers) as operand we had to use the `eax` constraint specifier.
> **Note**: unlike other operand types, explicit register operands cannot be used in the template string: you can't use `{}` and should write the register name directly instead. Also, they must appear at the end of the operand list after all other operand types.
Consider this example which uses the x86 `mul` instruction:
```rust
use std::arch::asm;
fn mul(a: u64, b: u64) -> u128 {
let lo: u64;
let hi: u64;
unsafe {
asm!(
// The x86 mul instruction takes rax as an implicit input and writes
// the 128-bit result of the multiplication to rax:rdx.
"mul {}",
in(reg) a,
inlateout("rax") b => lo,
lateout("rdx") hi
);
}
((hi as u128) << 64) + lo as u128
}
```
This uses the `mul` instruction to multiply two 64-bit inputs with a 128-bit result.
The only explicit operand is a register, that we fill from the variable `a`.
The second operand is implicit, and must be the `rax` register, which we fill from the variable `b`.
The lower 64 bits of the result are stored in `rax` from which we fill the variable `lo`.
The higher 64 bits are stored in `rdx` from which we fill the variable `hi`.
## Clobbered registers
In many cases inline assembly will modify state that is not needed as an output.
Usually this is either because we have to use a scratch register in the assembly or because instructions modify state that we don't need to further examine.
This state is generally referred to as being "clobbered".
We need to tell the compiler about this since it may need to save and restore this state around the inline assembly block.
```rust
use core::arch::asm;
fn main() {
// three entries of four bytes each
let mut name_buf = [0_u8; 12];
// String is stored as ascii in ebx, edx, ecx in order
// Because ebx is reserved, we get a scratch register and move from
// ebx into it in the asm. The asm needs to preserve the value of
// that register though, so it is pushed and popped around the main asm
// (in 64 bit mode for 64 bit processors, 32 bit processors would use ebx)
unsafe {
asm!(
"push rbx",
"cpuid",
"mov [{0}], ebx",
"mov [{0} + 4], edx",
"mov [{0} + 8], ecx",
"pop rbx",
// We use a pointer to an array for storing the values to simplify
// the Rust code at the cost of a couple more asm instructions
// This is more explicit with how the asm works however, as opposed
// to explicit register outputs such as `out("ecx") val`
// The *pointer itself* is only an input even though it's written behind
in(reg) name_buf.as_mut_ptr(),
// select cpuid 0, also specify eax as clobbered
inout("eax") 0 => _,
// cpuid clobbers these registers too
out("ecx") _,
out("edx") _,
);
}
let name = core::str::from_utf8(&name_buf).unwrap();
println!("CPU Manufacturer ID: {}", name);
}
```
In the example above we use the `cpuid` instruction to read the CPU manufacturer ID.
This instruction writes to `eax` with the maximum supported `cpuid` argument and `ebx`, `esx`, and `ecx` with the CPU manufacturer ID as ASCII bytes in that order.
Even though `eax` is never read we still need to tell the compiler that the register has been modified so that the compiler can save any values that were in these registers before the asm. This is done by declaring it as an output but with `_` instead of a variable name, which indicates that the output value is to be discarded.
This code also works around the limitation that `ebx` is a reserved register by LLVM. That means that LLVM assumes that it has full control over the register and it must be restored to its original state before exiting the asm block, so it cannot be used as an output. To work around this we save the register via `push`, read from `ebx` inside the asm block into a temporary register allocated with `out(reg)` and then restoring `ebx` to its original state via `pop`. The `push` and `pop` use the full 64-bit `rbx` version of the register to ensure that the entire register is saved. On 32 bit targets the code would instead use `ebx` in the `push`/`pop`.
This can also be used with a general register class (e.g. `reg`) to obtain a scratch register for use inside the asm code:
```rust
use std::arch::asm;
// Multiply x by 6 using shifts and adds
let mut x: u64 = 4;
unsafe {
asm!(
"mov {tmp}, {x}",
"shl {tmp}, 1",
"shl {x}, 2",
"add {x}, {tmp}",
x = inout(reg) x,
tmp = out(reg) _,
);
}
assert_eq!(x, 4 * 6);
```
## Symbol operands and ABI clobbers
By default, `asm!` assumes that any register not specified as an output will have its contents preserved by the assembly code. The [`clobber_abi`] argument to `asm!` tells the compiler to automatically insert the necessary clobber operands according to the given calling convention ABI: any register which is not fully preserved in that ABI will be treated as clobbered. Multiple `clobber_abi` arguments may be provided and all clobbers from all specified ABIs will be inserted.
[`clobber_abi`]: ../../reference/inline-assembly.html#abi-clobbers
```rust
use std::arch::asm;
extern "C" fn foo(arg: i32) -> i32 {
println!("arg = {}", arg);
arg * 2
}
fn call_foo(arg: i32) -> i32 {
unsafe {
let result;
asm!(
"call *{}",
// Function pointer to call
in(reg) foo,
// 1st argument in rdi
in("rdi") arg,
// Return value in rax
out("rax") result,
// Mark all registers which are not preserved by the "C" calling
// convention as clobbered.
clobber_abi("C"),
);
result
}
}
```
## Register template modifiers
In some cases, fine control is needed over the way a register name is formatted when inserted into the template string. This is needed when an architecture's assembly language has several names for the same register, each typically being a "view" over a subset of the register (e.g. the low 32 bits of a 64-bit register).
By default the compiler will always choose the name that refers to the full register size (e.g. `rax` on x86-64, `eax` on x86, etc).
This default can be overriden by using modifiers on the template string operands, just like you would with format strings:
```rust
use std::arch::asm;
let mut x: u16 = 0xab;
unsafe {
asm!("mov {0:h}, {0:l}", inout(reg_abcd) x);
}
assert_eq!(x, 0xabab);
```
In this example, we use the `reg_abcd` register class to restrict the register allocator to the 4 legacy x86 registers (`ax`, `bx`, `cx`, `dx`) of which the first two bytes can be addressed independently.
Let us assume that the register allocator has chosen to allocate `x` in the `ax` register.
The `h` modifier will emit the register name for the high byte of that register and the `l` modifier will emit the register name for the low byte. The asm code will therefore be expanded as `mov ah, al` which copies the low byte of the value into the high byte.
If you use a smaller data type (e.g. `u16`) with an operand and forget the use template modifiers, the compiler will emit a warning and suggest the correct modifier to use.
## Memory address operands
Sometimes assembly instructions require operands passed via memory addresses/memory locations.
You have to manually use the memory address syntax specified by the target architecture.
For example, on x86/x86_64 using Intel assembly syntax, you should wrap inputs/outputs in `[]` to indicate they are memory operands:
```rust
use std::arch::asm;
fn load_fpu_control_word(control: u16) {
unsafe {
asm!("fldcw [{}]", in(reg) &control, options(nostack));
}
}
```
## Labels
Any reuse of a named label, local or otherwise, can result in an assembler or linker error or may cause other strange behavior. Reuse of a named label can happen in a variety of ways including:
- explicitly: using a label more than once in one `asm!` block, or multiple times across blocks.
- implicitly via inlining: the compiler is allowed to instantiate multiple copies of an `asm!` block, for example when the function containing it is inlined in multiple places.
- implicitly via LTO: LTO can cause code from *other crates* to be placed in the same codegen unit, and so could bring in arbitrary labels.
As a consequence, you should only use GNU assembler **numeric** [local labels] inside inline assembly code. Defining symbols in assembly code may lead to assembler and/or linker errors due to duplicate symbol definitions.
Moreover, on x86 when using the default Intel syntax, due to [an LLVM bug], you shouldn't use labels exclusively made of `0` and `1` digits, e.g. `0`, `11` or `101010`, as they may end up being interpreted as binary values. Using `options(att_syntax)` will avoid any ambiguity, but that affects the syntax of the _entire_ `asm!` block. (See [Options](#options), below, for more on `options`.)
```rust
use std::arch::asm;
let mut a = 0;
unsafe {
asm!(
"mov {0}, 10",
"2:",
"sub {0}, 1",
"cmp {0}, 3",
"jle 2f",
"jmp 2b",
"2:",
"add {0}, 2",
out(reg) a
);
}
assert_eq!(a, 5);
```
This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`.
This example shows a few things:
- First, that the same number can be used as a label multiple times in the same inline block.
- Second, that when a numeric label is used as a reference (as an instruction operand, for example), the suffixes “b” (“backward”) or ”f” (“forward”) should be added to the numeric label. It will then refer to the nearest label defined by this number in this direction.
[local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels
[an LLVM bug]: https://bugs.llvm.org/show_bug.cgi?id=36144
## Options
By default, an inline assembly block is treated the same way as an external FFI function call with a custom calling convention: it may read/write memory, have observable side effects, etc. However, in many cases it is desirable to give the compiler more information about what the assembly code is actually doing so that it can optimize better.
Let's take our previous example of an `add` instruction:
```rust
use std::arch::asm;
let mut a: u64 = 4;
let b: u64 = 4;
unsafe {
asm!(
"add {0}, {1}",
inlateout(reg) a, in(reg) b,
options(pure, nomem, nostack),
);
}
assert_eq!(a, 8);
```
Options can be provided as an optional final argument to the `asm!` macro. We specified three options here:
- `pure` means that the asm code has no observable side effects and that its output depends only on its inputs. This allows the compiler optimizer to call the inline asm fewer times or even eliminate it entirely.
- `nomem` means that the asm code does not read or write to memory. By default the compiler will assume that inline assembly can read or write any memory address that is accessible to it (e.g. through a pointer passed as an operand, or a global).
- `nostack` means that the asm code does not push any data onto the stack. This allows the compiler to use optimizations such as the stack red zone on x86-64 to avoid stack pointer adjustments.
These allow the compiler to better optimize code using `asm!`, for example by eliminating pure `asm!` blocks whose outputs are not needed.
See the [reference](../../reference/inline-assembly.html) for the full list of available options and their effects.

View File

@ -1 +0,0 @@
# Unsafe todo

View File

@ -1,145 +0,0 @@
# Variables
### Binding and mutablity
🌟 A variable can be used only if it has been initialized.
```rust,editable
// fix the error below with least modifying
fn main() {
let x: i32; // uninitialized but using, ERROR !
let y: i32; // uninitialized but also unusing, only warning
println!("{} is equal to 5", x);
}
```
🌟 Use `mut` to mark a variable as mutable.
```rust,editable
// fill the blanks in code to make it compile
fn main() {
let __ = 1;
__ += 2;
println!("{} is equal to 3", x);
}
```
### Scope
A scope is the range within the program for which the item is valid.
```rust,editable.
🌟
// fix the error below with least modifying
fn main() {
let x: i32 = 10;
{
let y: i32 = 5;
println!("The value of x is {} and value of y is {}", x, y);
}
println!("The value of x is {} and value of y is {}", x, y);
}
```
🌟🌟
```rust,editable
// fix the error
fn main() {
println!("{}, world", x);
}
fn define_x() {
let x = "hello";
}
```
### Shadowing
You can declare a new variable with the same name as a previous variable, here we can say **the first one is shadowed by the second one.
🌟🌟
```rust,editable
// only modify `assert_eq!` to make the `println!` work(print `42` in terminal)
fn main() {
let x: i32 = 5;
{
let x = 12;
assert_eq!(x, 5);
}
assert_eq!(x, 12);
let x = 42;
println!("{}", x); // Prints "42".
}
```
🌟🌟
```rust,editable
// remove a line in code to make it compile
fn main() {
let mut x: i32 = 1;
x = 7;
// shadowing and re-binding
let x = x;
x += 3;
let y = 4;
// shadowing
let y = "I can also be bound to text!";
}
```
### Unused varibles
fix the warning below with :
- 🌟 one way
- 🌟🌟 two ways
> Note: there are two ways you can use, but none of them is removing the line `let x = 1`
```rust,editable
fn main() {
let x = 1;
}
// warning: unused variable: `x`
```
### Destructuring
🌟🌟 We can use a pattern with `let` to destructure a tuple to separate variables.
> Tips: you can use Shadowing or Mutability
```rust,editable
// fix the error below with least modifying
fn main() {
let (x, y) = (1, 2);
x += 2;
assert_eq!(x, 3);
assert_eq!(y, 2);
}
```
### Destructuring assignments
Introducing in Rust 1.59: You can now use tuple, slice, and struct patterns as the left-hand side of an assignment.
🌟
> Note: the feature `Destructuring assignments` need 1.59 or higher Rust version
```rust,editable
fn main() {
let (x, y);
(x,..) = (3, 4);
[.., y] = [1, 2];
// fill the blank to make the code work
assert_eq!([x,y], __);
}
```

View File

@ -1 +0,0 @@
# Weak and Circle reference

View File

@ -1,38 +0,0 @@
# Rust By Practice
Practice Rust with easy to hard examples, exercises and small projects.
## Read it online
- [English](https://practice.rs)
- [Chinsese](https://zh.practice.rs)
## Why another?
[Rustlings](https://github.com/rust-lang/rustlings) and [Rust By Example](https://github.com/rust-lang/rust-by-example) are absolutely very nice, but we provides a little more:
1. We have more exercises of which the difficulty is from easy to hard
2. Covers more topics in Rust, such as **async/await, threads, sync primitives, optimizing and stand libraries** etc
3. Learning from small projects, for pratice
4. Both English and Chinese are supported
## How to use
- You can edit and run the exercises online
- **The goal is to make each exercise comipile with NO ERRORS and Panics !**
- Difficulty level: easy: 🌟 medium: 🌟🌟 hard: 🌟🌟🌟 hell: 🌟🌟🌟🌟
## Contributing
All kinds of contributions are welcomed, such as exercise proposing, typo and bug fixing.
## Buy me a ... 🌟?
Fo us, a github star will be better than a cup coffee, and it only costs you 0 cents :)