Rust笔记【2】

  1. vector
// 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),
];
  1. 字符串
// 创建空字符串
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() { ... }
  1. Hash Map

  2. 泛型,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();
}

(1)函数模板

// Rust模板定义类似C++中模板实例化的语法,没有template和typename关键字
// 函数和结构体模板定义:
// 在函数名、类名后面添加类型参数声明<T>,模板声明方式类似c++模板显式实例化,
// 声明和显式实例化形式一致(确认??)
// 使用模板,多数情况可以依赖编译器自动推断,不用显式指定类型参数


// 结构体方法模板定义:impl和结构体名后面同时添加类型参数声明<T>(2个<T>)


// 函数模板
fn largest<T> (list: &[T]) -> &T {...}

(2)结构体模板(类模板)

// 类模板
struct Point<T> {
	x: T,
	y: T,
}

模板类的非模板方法
模板类的模板方法

// 类方法的泛型模板,impl后面需要提供类型参数,传递给类名的类型参数
impl<T> Point<T> {
	// 非模板方法
	fn get_x(&self) -> &T {
		&self.x
	}

	// 模板方法
	fn move_forward<D>(&self, distance: D) {
		// &self.x += distance ????
	}
}

结构体(类)模板特化

// 特化类型的方法模板,impl后面不需要类型声明,仅在结构体名后面添加具体特化类型的声明<f32>
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,
		}
	}
}
  1. trait

定义trait

trait Shape {
	// 注意,最后默认都带上参数&self,便于定义结构体方法
    fn get_area(&self) -> i32;
    fn enlarge(&self, multiple: i32);

    // default implementation
    fn get_tag(&self) -> String {
        String::from("shape")
    }
}

为struct实现trait

struct Rect {
    x: i32,
    y: i32,
}

impl Shape for Rect {
    fn get_area(&self) -> i32 {
        // todo!()
        &self.x * &self.y
    }

    fn enlarge(&self, multiple: i32) {
        todo!()
    }
}

参数类型使用trait

// impl trait语法,impl trait-name,&impl trait-name
fn draw1(item: impl Shape) {
    println!("draw: {}", item.get_tag());
}

// trait bound 语法
fn draw2<T: Shape> (item: T) {
    println!("draw: {}", item.get_tag());
}

// 多个trait用+号连接
fn draw3<T: Shape + Display> (item: T) {
    println!("draw: {}", item.get_tag());
}

// 使用trait bound,多个类型,多个trait,可读性差
fn draw4<T: Shape + Display, U: Debug + Display> (t: T, u: U) {
    //...
}

// 使用where后置trait bound
fn draw5<T, U>(t: T, u: U)
where
    T: Shape + Display,
    U: Debug + Display
{
    //...
}

返回值使用trait

fn creat_shape() -> impl Shape {
    Rect {
        x: 5,
        y: 6
    }
}
  1. 生命周期
    生命周期,lifetime,是和引用类型关联的泛型参数。
    引用类型不控制变量的所有权,避免悬空引用。
    只有和引用相关的函数、struct定义时,才需要用到lifetime。
    多个应用的场景下,lifetime才有意义。
    每个引用参数都有一个对应的lifetime。
    输出lifetime可自动推导的情况:唯一输入lifetime和方法&self的lifetime。

语法示例,lifetime在函数、struct的语境中作为泛型参数使用,无法单独使用

&i32 // i32引用
&'a i32 // 具有生命周期'a的i32引用
&'a mut i32 // 具有生命周期'a的可变i32引用

函数中的lifetime
(1)泛型参数列表中,lifetime在typename之前。
(2)输出lifetime可自动推导的情况:唯一输入lifetime和方法&self的lifetime。

fn func<'a, 'b, T, S>(t: &'a T, s: &'b S) -> &'a T {
    t
}

struct中lifetime
(1)struct中有引用类型,就需要声明lifetime参数。
(2)定义结构体方法时,impl<>和struct-name<>后面都需要带上lifetime。
(3)如果定义中没有使用到lifetime参数,可使用匿名lifetime:'_ ,如下面的read()方法。

struct Book<'a> {
    text: &'a str
}

impl<'a> Book<'_> {
    fn read(&self) {
        println!("text: {}", &self.text);
    }
}

impl<'a> Book<'a> {
    fn get(&self) -> &'a str {
        &self.text
    }
}
  1. 闭包
// 定义函数语法,和闭包语法对比
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);
  1. 迭代器

// 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();
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

抓饼先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值