Rust By Practice 中get的琐碎知识
- 忽略对未使用变量的警告
1
2
3
4// method 1
// method 2
let _varname = 1 ; - 解构式赋值
1
2
3
4
5
6
7
8fn main() {
let (x, y);
(x, ..) = (3, 4);
(.., y) = (3, 4);
assert_eq!([x, y], [3, 2]);
println!("{} {}", x, y)
} - std::fmt
数字
i8 u8 … ::MAX
1
2assert_eq!(i8::MAX, 127);
assert_eq!(u8::MAX, 255);取模的快速思考可以用
移位
思考进制与数字类型显式声明 ; i32 , f64 , _ 可省略
1
2
3
4
5let x = 1_024 + 0b1111_0010 + 0o1723 + 0xffffff;
// 10 2 8 16
let x = 1_024_i32 ;
let x = 0b1111_0010_u32 ;浮点数精度
1
2assert!(0.1_f32 + 0.2_f32 == 0.3_f32);
assert!((0.1_f64 + 0.2 - 0.3).abs() < f64::EPSILON);像C系一样的溢出自动取模
1
2use std::num::Wrapping;
let v1 = (Wrapping(251_u8) + Wrapping(8)).0;GetTypeName
1
2
3
4fn type_of<T>(_: &T) -> String {
format!("{}", std::any::type_name::<T>())
}for循环 [)
1
2
3
4for i in -3..2 {
println!("{}", i);
}
// -3 -2 -1 0 1Range 与 RangeInclusive
1
2
3
4
5use std::ops::{Range, RangeInclusive};
fn main() {
assert_eq!((1..5), Range { start: 1, end: 5 }); // 不包含5
assert_eq!((1..=5), RangeInclusive::new(1, 5)); // 包含5
}println 打印数字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32let x = 5;
println!("{}", x);
println!("{:5}", x);
println!("{:.5}", x);
println!("{:5.5}", x);
println!("{:05.5}", x);
let x = 5.9;
println!("{}", x);
println!("{:5}", x);
println!("{:.5}", x);
println!("{:5.5}", x);
println!("{:05.5}", x);
let x = 15;
println!("{:b}", x);
println!("{:o}", x);
println!("{:x}", x);
/*
5
5
5
5
00005
5.9
5.9
5.90000
5.90000
5.90000
5.90000
1111
17
f
*/
字符,布尔,单元
- char - utf8 - size=4
- fn()默认返回()
- ()类型 size=0
1 | use std::mem::size_of_val; |
- 运算符表达式的返回值
- 赋值运算符: 返回空元组()
- 其他运算符: 返回运算结果
函数
- 每个参数都要指定类型
- 可以用!表示永不返回,常用于无限循环/panic!/unimplemented!()/todo!() , 这样的函数叫发散函数(Diverging function)
1
2
3
4
5
6fn never_return() -> ! {
loop{};
unimplemented!();
todo!();
panic!();
}
String
String::from() 栈(ptr,cap,len)->堆 ; &str 栈(ptr,len) -> ReadOnly
1 | let s1 = String::from("hello,"); |
- 字符串转义
1
2
3
4
5"\x73"
"\u{211D}"
r"disable \(Escape)"
r#"allow " "# // 亦用于多行字符串
r###"allow " and # "### - 字节字符串
1
2
3let bytestring: &[u8; 21] = b"this is a byte string";
//字节数组可以不是 UTF-8 格式
let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82\xbb";- to str may be failed
1
2
3if let Ok(my_str) = str::from_utf8(raw_bytestring) {
println!("And the same as text: '{}'", my_str);
}
- to str may be failed
- 切片必须在边界:按char(8byte)索引
1
2let s1 = String::from("hi,中国");
assert_eq!(h1, "中"); - 按UTF-8字打印
1
2
3for c in "你好,世界".chars() {
println!("{}", c)
} - 好用的utf8_slice: 按字索引
1
2
3
4
5
6
7use utf8_slice;
fn main() {
let s = "The 🚀 goes to the 🌑!";
let rocket = utf8_slice::slice(s, 4, 5);
// 结果是 "🚀"
}
array
- 声明,必须固定大小
- get方法返回Option<T>,非常安全,但直接使用下标不安全(越界时对于array直接通不过编译)
1 | let arr: [i32;5] = [1, 2, 3, 4, 5]; |
slice
占用空间: 3字(64位:16byte) [胖指针]
tuple
- Debug Trait 要求len<=12 宏源码
struct
Struct Data => 无 ;
- Debug
1
2
3
4
5
6
7
8
9
10
11fn 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
}
enum TODO
common: tag*n [i8/i32…]
fn,struct…: tag,fn…
Option<T> : T can be 0 => 同上 ; T cannot be 0 => no tag
- 显式指定值
- 枚举可以持有各种值
- 模式匹配get值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::Move { x: 1, y: 2 };
if let Message::Move { x: a, y: b } = msg {
assert_eq!(a, b - 1);
} else {
panic!("不要让这行代码运行!");
}
}
Box
ptr->[Heap]
流程控制
1 | fn main() { |
- break后可以加值
1
2
3
4
5
6
7
8
9
10
11fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
assert_eq!(result, 20);
}
模式匹配
- if let 左 Pattern 右 值
- struct模式匹配-部分匹配并赋值给变量
1
2
3
4
5
6
7
8
9match p {
Point { x, y: 0 } => println!("On the x axis at {}", x),
Point {
x: 0..=5,
y: y @ (10 | 20 | 30),
} => println!("TWO::: On the y axis at {}", y),
Point { x, y } => println!("On neither axis: ({}, {})", x, y),
}new_var @ ( pattern)
- 分支后可以if
1
2
3
4
5
6
7
8
9
10fn main() {
let num = Some(4);
let split = 5;
match num {
Some(x) if split > x => assert!(x < split),
Some(x) if split <= x => assert!(x >= split),
None => (),
Some(_) => unreachable!(),
}
} - 分支部分赋值
1
2
3(first, .., last) =>{
} - match修改ref或值
1
2
3
4
5
6
7
8
9
10
11
12
13fn main() {
let mut v = String::from("hello,");
let r = &mut v;
match r {
value => value.push_str(" world!"),
}
match v {
ref mut value => value.push_str("!!!!"),
}
println!("{}", v);
}
impl
- self 是self:&Self的语法糖
- 方法是实例的方法,关联函数是类型的关联函数
泛型
1 | struct A; |
- impl块中函数有不同于impl对应struct/enum的type
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}```
- 这两个等价
```rust
fn summary<T: Summary>(s: &T) -> String {
s.summarize()
}
fn summary(t: &impl Summary) -> String {
t.summarize()
} - 离谱的const泛型
目前,const 泛型参数只能使用以下形式的实参:
一个单独的 const 泛型参数
一个字面量 (i.e. 整数, 布尔值或字符).
一个具体的 const 表达式( 表达式中不能包含任何 泛型参数)
简言之就是为了保证const无论怎么传递都是一个const(T很奇怪,为什么不能生成好几个参数,那(std::mem::size_of::<T>())不就也是const了吗)
通过将&str包裹成以const size为长度的arr struct 让编译器在编译时就知道&str长度
1 | pub struct MinSlice<T, const N: usize> { |
注意一个泛型struct不是一个struct
1
2
3
4
5
6
7
8
9
10
11struct Array<T, const N: usize> {
data: [T; N],
}
fn main() {
let arrays = [ // Error
Array { data: [1, 2, 3] },// Array<i32,3>
Array { data: [1.0, 2.0, 3.0] }, // Array<f64,3>
Array { data: [1, 2] },// Array<i32,2>
];
}Example
1
2
3
4
5
6
7
8
9
10fn print_array<T: std::fmt::Debug, const N: usize>(arr: [T; N]) {
println!("{:?}", arr);
}
fn main() {
let arr = [1, 2, 3];
print_array(arr);
let arr = ["hello", "world"];
print_array(arr);
}Example2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fn check_size<T>(val: T)
where
Assert<{ core::mem::size_of::<T>() < 768 }>: IsTrue,
{
//...
}
// 修复 main 函数中的错误
fn main() {
check_size([0u8; 767]);
check_size([0i32; 191]);
// let a = ["hello你好"; ];
check_size(["hello你好"; 48]); // size of &str ?
check_size([(); 0].map(|_| "hello你好".to_string())); // size of String?
check_size(['中'; 3]); // size of char ?
}
pub enum Assert<const CHECK: bool> {}
pub trait IsTrue {}
impl IsTrue for Assert<true> {}
trait
- dyn特征对象 使用前提:对象安全(trait fn 返回类型不为Self或泛型) 返回实现同一个trait的不同类型
1
2
3
4
5
6
7fn random_animal(random_number: f64) -> Box<dyn Animal> {
if random_number < 0.5 {
Box::new(Sheep {})
} else {
Box::new(Sheep {})
}
}
1 | fn hatch_a_bird(typ: i32) -> Box<dyn Bird> { |
- bat Example
1
2
3
4
5
6fn static_dispatch<T: Foo>(x: T) {
println!("static_dispatch: {}", x.method());
}
fn dynamic_dispatch(x: &dyn Foo) {
x.method();
} - 关联类型(语法糖) : 以下两个等价
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17trait Contains {
type A; // A可以加上trait约束,如type A:Copy+std::fmt::Display;
type B;
fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
fn first(&self) -> i32;
fn last(&self) -> i32;
}
impl Contains for Container {
type A = i32;
type B = i32;
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
(&self.0 == number_1) && (&self.1 == number_2)
}
fn first(&self) -> i32 { self.0 }
fn last(&self) -> i32 { self.1 }
}
1 | trait Contains<A, B> { |
- 默认泛型类型参数: 以下三者等价(Self为默认泛型类型参数)
1
2
3impl<T: Sub<Output = T>> Sub<Point<T>> for Point<T> {}
impl<T: Sub<Output = T>> Sub<Self> for Point<T> {}
impl<T: Sub<Output = T>> Sub for Point<T> {} - 如何调用不同trait下的同名方法?
1
2
3
4
5// trait AgeWidget{fn get();}
// struct Form();
// impl AgeWidget for Form{fn get(){}}
AgeWidget::get(&form);
<Form as AgeWidget>::get(&form); - 用于数组
let birds: [Box<dyn Bird>; 2] = [Box::new(Duck), Box::new(Swan)];
或let birds: [&dyn Bird; 2] = [&a, &b];
- 用于函数参数
fn draw_with_ref(x: &dyn Draw){}
impl Debug for Struct
- Debug
- Deref
- Drop , 手动释放:std::mem::drop(),不可显示调用a.drop()
derive 派生
Supertraits特征继承
1 | trait subTrait: SuperTrait1+SuperTrait2{ |
孤儿原则 TODO
- PartialEq 部分Eq