// generic template
Vec<T>
// craete a empty vecetor
let v: Vec<i32) = Vec::new();
// use macro vec! to create a vector, which can detect the argument type
let v = vec![1, 2, 3];
v.push(5);
v.pop();
// 使用[],如果索引越界(无效),则panic
let n: &i32 = &v[2];
// 使用get()方法,返回Option类型,可以处理索引越界(无效)的情况,此时返回None
let n: Option<&i32> = v.get(2);
let mut v = vec![1, 2, 3];
let first = &v[0];
// ...
v.push(5); // error,不能在相同作用域内同时存在可变和不可变引用
// 遍历vector,v和i不可变
for i in &v {
// ...
}
// 遍历vector,v和i可变
let mut v = vec![1, 2, 3];
for i in &mut v {
*i += 2;
}
// vector中只能保存单一类型
// 可使用enum保存多种类型,前提是多种类型是已知的、明确的
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
// 创建空字符串
let s = String::new();
// 使用字面量创建字符串,to_string()方法
let mut s = "hello".to_string();
// push string
s.push_str(" world"); // s: hello world
// push char
s.push('!'); // hello world!
// + operator
// 第一个参数移动,第二个参数引用
let s3 = s1 + &s2;
// +运算符使用了add函数
// 如果第二个参数类型是&String,会强制转换为&Str(Deref强制转换)
fn add(self, s: &str) -> String {...}
// 复杂字符串拼接用format!宏
let s = format!("{s1}-{s2}+{s3}");
// 不支持索引字符串,
let h = s[0]; // ERROR
// 可以使用字符串slice引用部分字符串
// Unicode使用可变字节数保存字母,如果索引值不是完整字母,则panic,慎用
let s = &hello[0..4];
// 遍历字符串
for c in "hello".chars() { ... }
for c in "hello".bytes() { ... }
Hash Map
泛型,generic,template
// C++ 模板语法,和Rust对比参考
template <typename T, typename S>
T func(S s) {
cout << "input: " << s << endl;
return s.data();
}
template <typename T>
void func2(T t) {
cout << "input 2: " << t << endl;
}
template <typename T>
struct Temp
{
T data;
Temp(T&& d): data(d) {}
void output() {
cout << "data: " << data << endl;
}
};
int main()
{
string str = "hi boy";
// 1. 函数模板
// 返回值类型不能自动推导,需要显式的进行模板实例化
char* ret = func<char*, string>(str);
// 如果没有返回类型,不需要推导,则不用显式指定模板类型参数,默认函数模板会进行类型的自动推导
// 以下3中方式是等效的
func2(str);
func2<>(str);
func2<string>(str>;
// 2. 类模板
// 显式或隐式实例化类模板
Temp<string> temp(str);
Temp temp(str);
temp.output();
}
// Rust模板定义类似C++中模板实例化的语法,没有template和typename关键字
// 函数和结构体模板定义:
// 在函数名、类名后面添加类型参数声明,模板声明方式类似c++模板显式实例化,
// 声明和显式实例化形式一致(确认??)
// 使用模板,多数情况可以依赖编译器自动推断,不用显式指定类型参数
// 结构体方法模板定义:impl和结构体名后面同时添加类型参数声明(2个)
// 函数模板
fn largest<T> (list: &[T]) -> &T {...}
// 类模板
struct Point<T> {
x: T,
y: T,
}
// 类方法的泛型模板,impl后面需要提供类型参数,传递给类名的类型参数
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
// 特化类型的方法模板,impl后面不需要类型声明,仅在结构体名后面添加具体特化类型的声明
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
// impl中指定的类型参数,不同于方法名后面的类型参数,可以分别指定
// impl中的类型参数是类型的类型参数(下例中和Point指定的类型参数一致)
impl<X1, Y1> Point<X1, Y1> {
fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
Point {
x: self.x,
y: other.y,
}
}
}
// 定义函数语法,和闭包语法对比
fn function(x: i32) -> i32 { x+1 }
// 闭包完整定义和函数fn定义类似,差别:
// 1- 把参数列表()换为||
// 2- 给闭包变量赋值时末尾需要分号,如果在入参时定义匿名闭包,不需要分号
let lambda1 = |x: i32| -> i32 { x+1 };
// 省略类型:可省略入参和返回值类型,由编译器推断
// 在作用域内,必须被调用,编译器才能推断类型。如果只有定义,没有调用,会报错。
let lambda2 = |x| { x+1 };
// 省略类型和大括号:只有一个表达式,可省略函数体的大括号{}
let lambda3 = |x| x+1;
// 在一个作用域内,如果可以推断不同的类型,报错
// 注意:闭包只是省略类型,由编译器推断出一种类型,不同于C++中的泛型lambda,后者可以生成适配多种类型的函数
let closure = |x| x;
let a = closure(String::from("hello"));
let b = closure(123);
// 闭包有3中捕获变量的方式:不可变引用、可变引用、移动所有权
let list = Vec![1, 2, 3];
let only_borrow = || println!("readonly: {:?}", list);
let mut borrows_mutably = || list.push(7);
thread::spawn(move || println!("move: {:?}", list)).join().unwrap();
3种捕获方式,可对应3种trait:
1) FnOnce:
被调用一次,
所有闭包至少实现这个trait,
将捕获的值移出闭包体,只实现FnOnce trait
2)FnMut
不会将捕获值移出闭包体
可能会修改捕获值
可调用多次
3)Fn
适用于捕获值既不移出闭包体,也不会修改
多次调用
适用于多次并发场景
// Option的unwrap_or_else方法定义
impl<T> Option<T> {
pub fn unwrap_or_else<F> (self, f:F) -> T
where
F: FnOnce() -> T
{
match self {
Some(x) => x,
None => f(),
}
}
}
Rust种函数也是对象,在需要闭包实参的地方,也可以传函数或者方法名:
unwrap_or_else(Vec::new)
#[derive(Debug)]
struct Rect {
width: i32,
height: i32,
}
let mut RectList = vec![
Rect { width: 1, height: 2, },
Rect { width: 10, height: 20, },
Rect { width: 100, height: 200, },
];
// 通过闭包,对结构体的一个成员进行排序
list.sort_by_key(|r| r.width);
// Iterator trait只要求定义一个方法:next
// type Item和Self::Item定义trait关联类型
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
...
}
let v = vec![1, 2, 3];
let i = v.iter(); // 不可变引用
let i = v.iter_mut(); // 可变引用
let i = v.into_iter(); // 获取所有权
for val in i { ... }
适配器是惰性的,需要调用消费适配器的方法,才会实际执行。
let total: i32 = v.iter().sum();
迭代器适配器,把当前迭代器转换为不同类型迭代器,可链式调用。
let v2: Vec<_> = v1.iter().map(|x| x+1).collect();
let v3: Vec<_> = v1.into_iter().filter(|x| x>10).collect();