update repo layout

This commit is contained in:
sunface
2022-03-23 20:04:00 +08:00
parent e2d3027b60
commit c1d8b06518
111 changed files with 958 additions and 4 deletions

View File

@@ -0,0 +1,105 @@
# Convert by `as`
Rust provides no implicit type conversion(coercion) between primitive types. But explicit type conversions can be performed using the `as` keyword.
1. 🌟
```rust,editable
// FIX the errors and FILL in the blank
// DON'T remove any code
fn main() {
let decimal = 97.123_f32;
let integer: __ = decimal as u8;
let c1: char = decimal as char;
let c2 = integer as char;
assert_eq!(integer, 'b' as u8);
println!("Success!")
}
```
2. 🌟🌟 By default, overflow will cause compile errors, but we can add an global annotation to suppress these errors.
```rust,editable
fn main() {
assert_eq!(u8::MAX, 255);
// the max of `u8` is 255 as shown above.
// so the below code will cause an overflow error: literal out of range for `u8`.
// PLEASE looking for clues within compile errors to FIX it.
// DON'T modify any code in main.
let v = 1000 as u8;
println!("Success!")
}
```
3. 🌟🌟 when casting any value to an unsigned type `T`, `T::MAX + 1` is added or subtracted until the value fits into the new type.
```rust,editable
fn main() {
assert_eq!(1000 as u16, __);
assert_eq!(1000 as u8, __);
// For positive numbers, this is the same as the modulus
println!("1000 mod 256 is : {}", 1000 % 256);
assert_eq!(-1_i8 as u8, __);
// Since Rust 1.45, the `as` keyword performs a *saturating cast*
// when casting from float to int. If the floating point value exceeds
// the upper bound or is less than the lower bound, the returned value
// will be equal to the bound crossed.
assert_eq!(300.1_f32 as u8, __);
assert_eq!(-100.1_f32 as u8, __);
// This behavior incurs a small runtime cost and can be avoided
// with unsafe methods, however the results might overflow and
// return **unsound values**. Use these methods wisely:
unsafe {
// 300.0 is 44
println!("300.0 is {}", 300.0_f32.to_int_unchecked::<u8>());
// -100.0 as u8 is 156
println!("-100.0 as u8 is {}", (-100.0_f32).to_int_unchecked::<u8>());
// nan as u8 is 0
println!("nan as u8 is {}", f32::NAN.to_int_unchecked::<u8>());
}
}
```
4. 🌟🌟🌟 Raw pointer can be converted to memory address (integer) and vice versa
```rust,editable
// FILL in the blanks
fn main() {
let mut values: [i32; 2] = [1, 2];
let p1: *mut i32 = values.as_mut_ptr();
let first_address: usize = p1 __;
let second_address = first_address + 4; // 4 == std::mem::size_of::<i32>()
let p2: *mut i32 = second_address __; // p2 points to the 2nd element in values
unsafe {
// add one to the second element
__
}
assert_eq!(values[1], 3);
println!("Success!")
}
```
5. 🌟🌟🌟
```rust,editable
fn main() {
let arr :[u64; 13] = [0; 13];
assert_eq!(std::mem::size_of_val(&arr), 8 * 13);
let a: *const [u64] = &arr;
let b = a as *const [u8];
unsafe {
assert_eq!(std::mem::size_of_val(&*b), __)
}
println!("Success!")
}
```

View File

@@ -0,0 +1,171 @@
# From/Into
The `From` trait allows for a type to define how to create itself from another type, hence providing a very simple mechanism for converting between several types.
The `From` and `Into` traits are inherently linked, and this is actually part of its implementation. It means if we write something like this: `impl From<T> for U`, then we can use
`let u: U = U::from(T)` or `let u:U = T.into()`.
The `Into` trait is simply the reciprocal of the `From` trait. That is, if you have implemented the `From` trait for your type, then the `Into` trait will be automatically implemented for the same type.
Using the `Into` trait will typically require the type annotations as the compiler is unable to determine this most of the time.
For example we can easily convert `&str` into `String` :
```rust
fn main() {
let my_str = "hello";
// three conversions below all depends on the fact: String implements From<&str>:
let string1 = String::from(my_str);
let string2 = my_str.to_string();
// explict type annotation is required here
let string3: String = my_str.into();
}
```
because the standard library has already implemented this for us : `impl From<&'_ str> for String` .
Some implementations of `From` trait can be found [here](https://doc.rust-lang.org/stable/std/convert/trait.From.html#implementors).
1. 🌟🌟🌟
```rust,editable
fn main() {
// impl From<bool> for i32
let i1:i32 = false.into();
let i2:i32 = i32::from(false);
assert_eq!(i1, i2);
assert_eq!(i1, 0);
// FIX the error in two ways
// 1. impl From<char> for ? , maybe you should check the docs mentiond above to find the answer
// 2. a keyword from the last chapter
let i3: i32 = 'a'.into();
// FIX the error in two ways
let s: String = 'a' as String;
println!("Success!")
}
```
### Implement `From` for custom types
2. 🌟🌟
```rust,editable
// From is now included in `std::prelude`, so there is no need to introduce it into the current scope
// use std::convert::From;
#[derive(Debug)]
struct Number {
value: i32,
}
impl From<i32> for Number {
// IMPLEMENT `from` method
}
// FILL in the blanks
fn main() {
let num = __(30);
assert_eq!(num.value, 30);
let num: Number = __;
assert_eq!(num.value, 30);
println!("Success!")
}
```
3. 🌟🌟🌟 When performing error handling it is often useful to implement `From` trait for our own error type. Then we can use `?` to automatically convert the underlying error type to our own error type.
```rust,editable
use std::fs;
use std::io;
use std::num;
enum CliError {
IoError(io::Error),
ParseError(num::ParseIntError),
}
impl From<io::Error> for CliError {
// IMPLEMENT from method
}
impl From<num::ParseIntError> for CliError {
// IMPLEMENT from method
}
fn open_and_parse_file(file_name: &str) -> Result<i32, CliError> {
// ? automatically converts io::Error to CliError
let contents = fs::read_to_string(&file_name)?;
// num::ParseIntError -> CliError
let num: i32 = contents.trim().parse()?;
Ok(num)
}
fn main() {
println!("Success!")
}
```
### TryFrom/TryInto
Similar to `From` and `Into`, `TryFrom` and `TryInto` are generic traits for converting between types.
Unlike `From/Into`, `TryFrom` and `TryInto` are used for fallible conversions and return a `Result` instead of a plain value.
4. 🌟🌟
```rust,editable
// TryFrom and TryInto are included in `std::prelude`, so there is no need to introduce it into the current scope
// use std::convert::TryInto;
fn main() {
let n: i16 = 256;
// Into trait has a method `into`,
// hence TryInto has a method ?
let n: u8 = match n.__() {
Ok(n) => n,
Err(e) => {
println!("there is an error when converting: {:?}, but we catch it", e.to_string());
0
}
};
assert_eq!(n, __);
println!("Success!")
}
```
5. 🌟🌟🌟
```rust,editable
#[derive(Debug, PartialEq)]
struct EvenNum(i32);
impl TryFrom<i32> for EvenNum {
type Error = ();
// IMPLEMENT `try_from`
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value % 2 == 0 {
Ok(EvenNum(value))
} else {
Err(())
}
}
}
fn main() {
assert_eq!(EvenNum::try_from(8), Ok(EvenNum(8)));
assert_eq!(EvenNum::try_from(5), Err(()));
// FILL in the blanks
let result: Result<EvenNum, ()> = 8i32.try_into();
assert_eq!(result, __);
let result: Result<EvenNum, ()> = 5i32.try_into();
assert_eq!(result, __);
println!("Success!")
}
```

View File

@@ -0,0 +1,5 @@
# Ownership and Borrowing
Learning resources:
- English: [Standary library](https://std.rs)
- 简体中文: [Rust语言圣经 - 所有权与借用](https://course.rs/basic/converse.html)

View File

@@ -0,0 +1,164 @@
# Others
### Convert any type to String
To convert any type to `String`, you can simply the `ToString` trait for that type. Rather than doing that directly, you should implement the `fmt::Display` trait which will automatically provides `ToString` and also allows you to print the type with `println!`.
1. 🌟🌟
```rust,editable
use std::fmt;
struct Point {
x: i32,
y: i32,
}
impl fmt::Display for Point {
// IMPLEMENT fmt method
}
fn main() {
let origin = Point { x: 0, y: 0 };
// FILL in the blanks
assert_eq!(origin.__, "The point is (0, 0)");
assert_eq!(format!(__), "The point is (0, 0)");
println!("Success!")
}
```
### Parse a String
2. 🌟🌟🌟 We can use `parse` method to convert a `String` into a `i32` number, this is becuase `FromStr` is implemented for `i32` type in standard library: `impl FromStr for i32`
```rust,editable
// To use `from_str` method, you needs to introduce this trait into the current scope.
use std::str::FromStr;
fn main() {
let parsed: i32 = "5".__.unwrap();
let turbo_parsed = "10".__.unwrap();
let from_str = __.unwrap();
let sum = parsed + turbo_parsed + from_str;
assert_eq!(sum, 35);
println!("Success!")
}
```
3. 🌟🌟 We can also implement the `FromStr` trait for our custom types
```rust,editable
use std::str::FromStr;
use std::num::ParseIntError;
#[derive(Debug, PartialEq)]
struct Point {
x: i32,
y: i32
}
impl FromStr for Point {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let coords: Vec<&str> = s.trim_matches(|p| p == '(' || p == ')' )
.split(',')
.collect();
let x_fromstr = coords[0].parse::<i32>()?;
let y_fromstr = coords[1].parse::<i32>()?;
Ok(Point { x: x_fromstr, y: y_fromstr })
}
}
fn main() {
// FILL in the blanks in two ways
// DON'T change code anywhere else
let p = __;
assert_eq!(p.unwrap(), Point{ x: 3, y: 4} );
println!("Success!")
}
```
### Deref
You can find all the examples and exercises of the `Deref` trait [here](https://practice.rs/smart-pointers/deref.html).
### transmute
`std::mem::transmute` is a **unsafe function** can be used to reinterprets the bits of a value of one type as another type. Both of the orginal and the result types must have the same size and neither of them can be invalid.
`transmute` is semantically equivalent to a bitwise move of one type into another. It copies the bits from the source value into the destination value, then forgets the original, seems equivalent to C's `memcpy` under the hood.
So, **`transmute` is incredibly unsafe !** The caller has to ensure all the safes himself!
#### Examples
1. `transmute` can be used to turn a pointer into a function pointer, this is not portable on machines where function pointer and data pointer have different sizes.
```rust,editable
fn foo() -> i32 {
0
}
fn main() {
let pointer = foo as *const ();
let function = unsafe {
std::mem::transmute::<*const (), fn() -> i32>(pointer)
assert_eq!(function(), 0);
}
```
2. Extending a lifetime or shortening the lifetime of an invariant is an advanced usage of `transmute`, yeah, **very unsafe Rust!**.
```rust,editable
struct R<'a>(&'a i32);
unsafe fn extend_lifetime<'b>(r: R<'b>) -> R<'static> {
std::mem::transmute::<R<'b>, R<'static>>(r)
}
unsafe fn shorten_invariant_lifetime<'b, 'c>(r: &'b mut R<'static>)
-> &'b mut R<'c> {
std::mem::transmute::<&'b mut R<'static>, &'b mut R<'c>>(r)
}
```
3. Rather than using `transmute`, you can use some alternatives instead.
```rust,editable
fn main() {
/*Turning raw bytes(&[u8]) to u32, f64, etc.: */
let raw_bytes = [0x78, 0x56, 0x34, 0x12];
let num = unsafe { std::mem::transmute::<[u8; 4], u32>(raw_bytes) };
// use `u32::from_ne_bytes` instead
let num = u32::from_ne_bytes(raw_bytes);
// or use `u32::from_le_bytes` or `u32::from_be_bytes` to specify the endianness
let num = u32::from_le_bytes(raw_bytes);
assert_eq!(num, 0x12345678);
let num = u32::from_be_bytes(raw_bytes);
assert_eq!(num, 0x78563412);
/*Turning a pointer into a usize: */
let ptr = &0;
let ptr_num_transmute = unsafe { std::mem::transmute::<&i32, usize>(ptr) };
// Use an `as` cast instead
let ptr_num_cast = ptr as *const i32 as usize;
/*Turning an &mut T into an &mut U: */
let ptr = &mut 0;
let val_transmuted = unsafe { std::mem::transmute::<&mut i32, &mut u32>(ptr) };
// Now, put together `as` and reborrowing - note the chaining of `as`
// `as` is not transitive
let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) };
/*Turning an &str into a &[u8]: */
// this is not a good way to do this.
let slice = unsafe { std::mem::transmute::<&str, &[u8]>("Rust") };
assert_eq!(slice, &[82, 117, 115, 116]);
// You could use `str::as_bytes`
let slice = "Rust".as_bytes();
assert_eq!(slice, &[82, 117, 115, 116]);
// Or, just use a byte string, if you have control over the string
// literal
assert_eq!(b"Rust", &[82, 117, 115, 116]);
}
```