mirror of
https://github.com/sunface/rust-by-practice.git
synced 2025-06-23 04:29:41 +00:00
update project structure
This commit is contained in:
@ -1 +0,0 @@
|
||||
practice.rs
|
1
en/book
1
en/book
Submodule en/book deleted from 036e93c39b
20
en/book.toml
20
en/book.toml
@ -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"
|
21
en/deploy
21
en/deploy
@ -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
1
en/src/.gitignore
vendored
@ -1 +0,0 @@
|
||||
book
|
@ -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)
|
@ -1 +0,0 @@
|
||||
# async and await!
|
@ -1 +0,0 @@
|
||||
# Future
|
@ -1 +0,0 @@
|
||||
# Async/Await
|
@ -1 +0,0 @@
|
||||
# Pin and Unpin
|
@ -1 +0,0 @@
|
||||
# Stream
|
@ -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);
|
||||
}
|
||||
```
|
@ -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
|
||||
|
||||
}
|
||||
```
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
```
|
@ -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;
|
||||
}
|
||||
```
|
@ -1 +0,0 @@
|
||||
# Circle reference and Self referential
|
@ -1 +0,0 @@
|
||||
# HashMap
|
@ -1 +0,0 @@
|
||||
# Collection Types
|
@ -1 +0,0 @@
|
||||
# Vector
|
@ -1 +0,0 @@
|
||||
# Comments and Docs
|
@ -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];
|
||||
}
|
||||
|
||||
```
|
||||
|
@ -1 +0,0 @@
|
||||
# enum
|
@ -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)
|
||||
|
||||
|
@ -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]
|
||||
}
|
||||
```
|
@ -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 "🚀"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
@ -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 don’t 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);
|
||||
}
|
||||
```
|
@ -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)
|
||||
}
|
||||
```
|
@ -1 +0,0 @@
|
||||
# Crate
|
@ -1 +0,0 @@
|
||||
# Crate and module
|
@ -1 +0,0 @@
|
||||
# Module
|
@ -1 +0,0 @@
|
||||
# use and pub
|
@ -1 +0,0 @@
|
||||
# Errors
|
@ -1 +0,0 @@
|
||||
# Flow Control
|
@ -1 +0,0 @@
|
||||
# Formatted output
|
@ -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);
|
||||
}
|
||||
```
|
@ -1 +0,0 @@
|
||||
# Functional programing
|
@ -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();
|
||||
```
|
@ -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);
|
||||
}
|
||||
```
|
@ -1 +0,0 @@
|
||||
# Functional Programming
|
@ -1 +0,0 @@
|
||||
# Advance Traits
|
@ -1 +0,0 @@
|
||||
# Generics
|
@ -1 +0,0 @@
|
||||
# Generics and Traits
|
@ -1 +0,0 @@
|
||||
# Trait Object
|
@ -1 +0,0 @@
|
||||
# Traits
|
@ -1 +0,0 @@
|
||||
# Global variables
|
@ -1 +0,0 @@
|
||||
# advance
|
@ -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
|
||||
```
|
@ -1 +0,0 @@
|
||||
# Lifetime
|
@ -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);
|
||||
}
|
||||
```
|
@ -1 +0,0 @@
|
||||
# macro
|
@ -1 +0,0 @@
|
||||
# Method
|
@ -1 +0,0 @@
|
||||
# newtype and Sized
|
@ -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 blank,dont 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
|
||||
}
|
||||
```
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
```
|
@ -1 +0,0 @@
|
||||
# Pattern Match
|
@ -1 +0,0 @@
|
||||
# match, if let
|
@ -1 +0,0 @@
|
||||
# Option destructing
|
@ -1 +0,0 @@
|
||||
# Patterns
|
@ -1 +0,0 @@
|
||||
# Result and panic
|
@ -1 +0,0 @@
|
||||
# panic!
|
@ -1 +0,0 @@
|
||||
# result and ?
|
@ -1 +0,0 @@
|
||||
# Self referential
|
@ -1 +0,0 @@
|
||||
# Box
|
@ -1 +0,0 @@
|
||||
# Cell and RefCell
|
@ -1 +0,0 @@
|
||||
# Deref
|
@ -1 +0,0 @@
|
||||
# Drop
|
@ -1 +0,0 @@
|
||||
# Smart pointers
|
@ -1 +0,0 @@
|
||||
# Rc and Arc
|
@ -1 +0,0 @@
|
||||
# Stand Library todo
|
@ -1 +0,0 @@
|
||||
# Assertions
|
@ -1,3 +0,0 @@
|
||||
# Benchmark
|
||||
|
||||
https://doc.rust-lang.org/unstable-book/library-features/test.html
|
@ -1 +0,0 @@
|
||||
# Tests
|
@ -1 +0,0 @@
|
||||
# Unit and Integration
|
@ -1 +0,0 @@
|
||||
# Write Tests
|
@ -1 +0,0 @@
|
||||
# Atomic
|
@ -1 +0,0 @@
|
||||
# Basic using
|
@ -1 +0,0 @@
|
||||
# Threads
|
@ -1 +0,0 @@
|
||||
# Message passing
|
@ -1 +0,0 @@
|
||||
# Send and Sync
|
@ -1 +0,0 @@
|
||||
# Sync
|
@ -1 +0,0 @@
|
||||
# Type Conversion
|
@ -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.
|
@ -1 +0,0 @@
|
||||
# Unsafe todo
|
@ -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], __);
|
||||
}
|
||||
```
|
@ -1 +0,0 @@
|
||||
# Weak and Circle reference
|
@ -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 :)
|
Reference in New Issue
Block a user