Rust
安装
略
Hello world
-
新建一个文件夹
-
创建main.rs文件
-
编写代码
fn main(){ println!("hello, world!"); }
-
编译运行
rustc main.rs
cargo
cargo是一个依赖管理和编译集成工具。
cargo --version
cargo 创建工程
cargo new hello_cargo
cargo.toml是依赖管理文件
编译
cargo build
运行
.\target\debug\hello_cargo.exe
or
cargo run
发布
cargo build --release
Guessing Game
-
创建新项目
cargo new gassing_game
-
运行一下
cargo run
-
输入输出
//使用标准库,不需要外部依赖的库 use std::io; fn main() { println!("Hello, world!"); println!("Please input a number!"); //定义一个字符串变量 let mut guess = String::new(); //获取标准输入 io::stdin().read_line(&mut guess).expect("Failed to read line"); //打印变量 println!("your input is {}",guess); }
-
导入随机数库
//cargo.toml [dependencies] rand = "0.8.3"
-
更新镜像源
在user/.cargo/config [source.crates-io] registry = "https://github.com/rust-lang/crates.io-index" replace-with = 'ustc' [source.ustc] registry = "git://mirrors.ustc.edu.cn/crates.io-index"
-
编译,下载依赖
cargo build
-
使用随机数
use rand::Rng; fn main() { let secret_number = rand::thread_rng().gen_range(1..101); println!("secret number is {}",secret_number); }
-
比较
use std::io; use rand::Rng; use std::cmp::Ordering; fn main() { println!("Hello, world!"); let secret_number = rand::thread_rng().gen_range(1..101); println!("secret number is {}",secret_number); println!("Please input a number!"); let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("Failed to read line"); let guess:u32 = guess.trim().parse().expect("please input a number!"); println!("You guessed {}",guess); match guess.cmp(&secret_number) { Ordering::Less => println!("too small!"), Ordering::Greater => println!("too big!"), Ordering::Equal => println!("You win!"), } }
-
循环
use std::io; use rand::Rng; use std::cmp::Ordering; fn main() { println!("Hello, world!"); let secret_number = rand::thread_rng().gen_range(1..101); // println!("secret number is {}",secret_number);a loop { println!("Please input a number:"); let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("Failed to read line"); let guess:u32 = guess.trim().parse().expect("please input a number!"); println!("You guessed {}",guess); match guess.cmp(&secret_number) { Ordering::Less => println!("too small!"), Ordering::Greater => println!("too big!"), Ordering::Equal => { println!("You win!"); break; } } } }
变量
-
创建变量项目
cargo new variables
-
未加mut的变量不可修改值
fn main() { println!("Hello, world!"); let mut x = 6; println!("x is {}",x); x=7; println!("x is {}",x); }
-
常量
const MAX_VALUE:i32 = 100; println!("max value is {}",MAX_VALUE);
-
shadow
let y = 4; let y = y +3; println!("y is {}",y);//7 { let y = y +2; println!("y is {}",y);//9 } println!("y is {}",y);//7 let str = "hello world"; let str = str.len(); println!("str's len is {}",str);
Data Types
let x = "46".parse().expect("not a number!");//illegal
let x:i32 = "46".parse().expect("not a number!");
integer
float
默认64位
bool
char
可以有表情
let em = '🆒';
Tuple
let tup = (1,2.3,'h');
let (x,y,z) = tup;
println!("x:{},y:{},z:{}",tup.0,tup.1,tup.2);
Array
let arr = [1,2,3,4,5];
let arr:[i32;5] = [1,2,3,4,5];
let arr = [0;100];//100 个 0
Never
一种数据类型,使用!表示,用于永远不可能有返回值的函数;
#![feature(never_type)]
fn foo()->i32 {
let x: ! = {
return 123
};
}
动态类型
零类型
大小为零ZST,空
底类型
Never的实现,无
Function
fn add(a:i32,b:i32)->i32{
let a = {
let a = 1;
a+1
};
a
}
control flow
if
类型一致;
condition只能是bool值
多于2个else if 使用match代替
fn main() {
println!("Hello, world!");
let condition = true;
if condition{
println!("condition is true!");
}else {
println!("condition is false!");
}
}
let x = if condition { 6 } else {"six"};//类型不一样,编译错误
let x = if condition { 6 } else {7};
loop
loop前面可以有标号
break只跳出一层、
fn test_loop(){
let mut count = 0;
'flag: loop {
println!("count is {}",count);
let mut remainder = 10;
loop {
println!("remainder = {}",remainder);
if remainder == 9 {
break;
}
if count ==2 {
break 'flag;
}
remainder -= 1;
}
count+=1;
}
println!("end count is {}",count);
}
while
while true不行,控制流分析。
fn while_true(x:i32)->i32{
while true {
return x+1;
}
x//resolve
}
fn test_while(){
let mut iter = 3;
while iter > 0 {
println!("iter is {}",iter);
iter -= 1;
}
}
for
本质是一个迭代器。
fn test_for(){
let a = [0;3];
for e in a {
println!("{}",e);
}
for e in (1..4).rev() {
println!("{}",e);
}
}
ownership
拥有权是针对堆区的数据来说的。
对于基本数据类型都是存在栈上,所以无关拥有权
fn main() {
println!("Hello, world!");
let x = 4;
let y =x;
println!("{}{}",x,y);
let s = "hello";
let s1 = s;
let flag = true;
let flag1 = flag;
}
对于堆上的数据,赋值、传参都会移交拥有权,对于这种操作叫做move, 。
fn string_onwership(){
let s = String::from("hello");
let s1 = s;
{
let s2 = s1;
}//s1,s2都不可用了
let s3 = gives_onwership();//s3借用了hello的拥有权
get_onwership(s3);//s3move给了函数
//s3不可用了
}
fn gives_onwership()->String{
let s = String::from("hello");
s
}
fn get_onwership(s:String){
let s1 = s;
}
引用
不移交拥有权
fn main() {
println!("Hello, world!");
let s = String::from("hello");
println!("{}",get_len(&s));
println!("{}",s);
}
fn get_len(s:&String)->usize{
s.len()
}
fn main() {
println!("Hello, world!");
let s = String::from("hello");
println!("{}",get_len(&s));
println!("{}",s);
let mut s1 = String::from("hello");
change(&mut s1);
println!("{}",s1);
}
fn change(s:&mut String){
s.push_str("hello");
}
可变引用只能有一次。
不可变引用可以有无限次。
fn quote(){
let mut s = String::from("hello");
let s1 = &mut s;
let s2 = &mut s;//错误
println!("{}{}",s2,s1);
}
fn quote(){
let mut s = String::from("hello");
let s1 = &mut s;
let s2 = &s1;
println!("{}{}",s2,s1);
}
可变变量不能同时存在可变引用和不可变引用,可以有多个不可变引用。
fn quote1(){
let mut my_str = String::from("A mutable string");
let first_unmutable_reference = &my_str;
let second_unmutable_reference = &my_str;
println!("{} {}", first_unmutable_reference, second_unmutable_reference);
}
可变变量不能同时存在多个可变引用。
可变引用移交所有权。
引用的传递叫借用。
切片
fn slice(){
let s = String::from("hello world");
let s1 = &s[1..2];
}
Struct
三种
-
正常具名结构体
-
元组
-
NewType
一个元组结构体只有一个字段的时候,我们称之为New Type 模式 struct Interger(i32); type Int = i32;
-
也可以使用type 关键字为一个类型创建别名
-
-
单元结构体:没有任何子段的结构体
define
struct Student {
email:char,
name:char,
age:f64,
}
struct Color(i16,u32,String);
initial
let mut a = Student{
email:String::from("123@163.com"),
name:String::from("gre"),
age:23.1,
};
let mut b = Student{
name:String::from("hello"),
..a
};//此方法会move
访问
println!("{}",a.age);
println!("{}",c.1);
方法
struct Rectangle{
width:u32,
height:u32,
}
impl Rectangle {
fn area(&self)->u32{
self.width * self.height
}
}
let rect = Rectangle{
width:10,
height:10,
};
println!("{}", rect.area());
let squ = Rectangle::square(4);
println!("{}",squ.area());
Enums
enum IpAddr {
V4(String),
V6,
}
fn main() {
let four = IpAddr::V4(String::from("127.0.0.1"));
// println!("{}",four);
}
option
Rust没有null
提供了Option,预导入了,可以直接使用。
pub enum Option<T> {
/// No value
#[lang = "None"]
#[stable(feature = "rust1", since = "1.0.0")]
None,
/// Some value `T`
#[lang = "Some"]
#[stable(feature = "rust1", since = "1.0.0")]
Some(#[stable(feature = "rust1", since = "1.0.0")] T),
}
let a:Option<i32> = Some(12);
let b:i32 = 2;
let c = a+b;//error 类型不同
match
if let
while let
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin:Coin)->u8{
match coin {
Coin::Penny=>1,
Coin::Nickel=>5,
Coin::Dime=>10,
Coin::Quarter=>{
20+5
}
}
}
fn plus_one(x:Option<i32>)->Option<i32>{
match x {
None => None,
Some(i)=>Some(i+1),
}
}
fn main() {
println!("Hello, world!");
println!("{}",value_in_cents(Coin::Nickel));
plus_one(Some(3));
}
let v = 0u8;
match v {
1 => println!("{}",1),
_ => (),
}
let v = Some(0u8);
if let Some(3) = v {
println!("three");
}
指针
- 原生指针
- 引用
- 函数指针
- 智能指针
原生指针
原生指针不安全,需要使用unsafe,两种
*const T
*mut T
fn main() {
let mut x = 10;
let ptr_x = &mut x as *mut i32;
let y = Box::new(20);
let ptr_y = &*y as *const i32;
unsafe{
*ptr_x += *ptr_y;
}
println!("x: {}",x);
}
Box是在堆上创建内容。
智能指针
非Rust独有,借鉴于C++
Rust默认将变量分配到栈空间,使用Box可以分配到堆空间,并且可以自动析构。
Box,可以通过*
解引用获取T
智能指针不止Box
#[derive(Debug)]
struct Point{
x:i32,
y:i32,
}
fn main() {
let p1 = Point{x:12,y:13};
let origin = Box::new(p1);
let p2 = *origin;
println!("{:?}",p2);
}
Package,Crate,Module
集合
- 线性:向量,双端队列,链表
- Key-Value:无序哈希表,有序哈希表
- 集合类型:无序集合,有序集合
- 优先队列:二叉堆(优先队列)
vector
- 标准库提供
- 可以存储多个值
- 只能存储相同数据类型
- 值在内存中连续存放
创建
fn main() {
println!("Hello, world!");
let v:Vec<i32> = Vec::new();
let v = vec![1,2,3];
}
更新
要么读,要么写
let mut v = vec![1,2,3];
v.push(2);
v.push(3);
v.push(4);
获取
let mut v = vec![1,2,3];
v.push(2);
v.push(3);
v.push(4);
let third:&i32 = &v[3];
println!("The third element is {}",third);
match v.get(3) {
Some(third) => println!("The third element is {}",third),
None => println!("None"),
}
let third:&i32 = &v[100];//panic
println!("The third element is {}",third);
match v.get(100) {//ok
Some(third) => println!("The third element is {}",third),
None => println!("None"),
}
遍历
let v = vec![1,2,3,4,5];
for ele in &v {
print!("{}",ele);
}
let mut v = v;
for ele in &mut v{
*ele += 5;
println!("{}",ele);
}
删除
- 出作用域就会删除
方不同数据类型
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
fn example(){
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Float(3.2),
SpreadsheetCell::Text(String::from("hello")),
];
}
String
- Byte的集合
- 一些方法
- 将byte解析为文本
- UTF-8
str
切片形式str
创建
fn create(){
let mut s = String::new();
let data = "initial contents";
let s = data.to_string();
let s1 = String::from("initial contents");
let utf_char = String::from("こんにちは、世界!");
}
更新
类比vector
使用+类似add方法
fn update(){
let mut s = String :: from("foo");
let str = String::from("bar");
s.push_str(&str);
s.push('l');
println!("{}",str);
}
拼接
fn cat(){
let s1 = String::from("hello");
let s2 = String::from("world");
let s3 = s1 + &s2;
println!("{}",s3);
let s4 = format!("{}-{}",s2,s3);
println!("{}",s4);
}
访问
字符簇
fn read(){
let s = String::from("hello world");
let a = s[1];//error
}
fn read(){
let s = String::from("hello world");
// let a = s[1];
let len = String::from("1234").len();//4
let len = String::from("ᖃᐅᔨᒪᒻᒪᕆᑦᑐᓄᑦ").len();//33
//Unicode 标量值
println!("{}",len);
}
fn type_check(){
let s = String::from("शुभाः दुनिया!");
for b in s.bytes(){
println!("{}",b);
}
for c in s.chars(){
println!("{}",c);
}
}
切割
fn slice(){
let hello = String::from("शुभाः दुनिया!");
let s = &hello[0..6];//4 panic, each char 3 bytes
println!("{}",s);
}
hashmap
- HashMap<K,V>
创建
- 堆上
use std::collections::HashMap;
fn create(){
let mut scores:HashMap<String,i32> = HashMap::new();
let mut scores = HashMap::new();
scores.insert(String::from("age"),23);
}
fn create2(){
let teams = vec![String::from("blue"),String::from("yellow")];
let initial_score = vec![10,50];
let scores:HashMap<_,_> = teams.iter().zip(initial_score.iter()).collect();
}
所有权
-
对于实现了copy trait的类型,值会被复制到HashMap中
-
对于拥有所有权的值(String),值会移动(move),所有权归HashMap
fn ownership(){ let key = String::from("jack"); let value = String::from("32"); let mut person = HashMap::new(); // person.insert(key, value); // println!("{}{}",key,value);//error person.insert(&key, &value); println!("{}{}",key,value);//ok }
获取
fn get(){
let mut scores = HashMap::new();
scores.insert(String::from("blue"), 10);
scores.insert(String::from("yellow"), 50);
let team_name = String::from("blue");
let score = scores.get(&team_name);
match score {
Some(s) => println!("score is {}",s),
None => println!("team not exist"),
}
}
遍历
fn traverse(){
let mut scores = HashMap::new();
scores.insert(String::from("blue"), 10);
scores.insert(String::from("yellow"), 50);
for (k,v) in &scores{
println!("{}-{}",k,v);
}
}
更新
- HashMap大小可变
- 每个k对应一个v
- 更新
- 存在,替换,保留,合并
- 不存在,添加
fn update(){
let mut scores = HashMap::new();
scores.insert(String::from("blue"), 10);
scores.insert(String::from("blue"), 25);
println!("{:?}",scores);
scores.entry(String::from("yellow")).or_insert(50);
scores.entry(String::from("blue")).or_insert(50);
println!("{:?}",scores);//{"blue": 25, "yellow": 50}
}
错误处理
- 编译错误并处理
- 错误分类
- 可恢复,文件未找到
- 不可恢复,超出index
- Rust没有异常处理机制
- 可恢复,Result
- 不可恢复:panic!宏
panic!
当panic!宏执行时:
- 程序打印错误信息
- 展开unwind,清理调用栈Stack
- 退出程序
可以选择终止调用栈:
- 不清理,直接退出
- 内存需要os进行清理
让程序文件更小,可以把展开改为终止:
cargo.toml的profile
- panic = ‘abort’
[profile.release]
panic = 'abort'
错误信息
RUST_BACKTRACE=1
fn main() {
// panic!("crash and burn");
let v = vec![1,2,3];
v[22];
println!("Hello, world!");
}
不带–release
Result
pub enum Result<T, E> {
Ok(#[stable(feature = "rust1", since = "1.0.0")] T),
Err(#[stable(feature = "rust1", since = "1.0.0")] E),
}
use std::fs::File;
fn example(){
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => {
panic!("error opening file {:?}",error);
}
};
}
use std::io::ErrorKind;
fn example2(){
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt"){
Ok(fc) => fc,
Err(e) => panic!("error creating file {:?}",e),
},
other_error => panic!("Error opening the file {:?}",other_error),
}
};
}
闭包
fn closure(){
let f = File::open("hello.txt").unwrap_or_else(|error|{
if error.kind() == ErrorKind::NotFound{
File::create("hello.txt").unwrap_or_else(|error|{
panic!("Error create file:{:?}",error);
})
}else {
panic!("Error opening file {:?}",error);
}
});
}
unwrap
match表达式的一个快捷方法;
- 如果是ok就返回ok的结果
- err返回panic!宏
fn example3(){
let f = File::open("hello.txt").unwrap();
}
expect
可以指定错误信息
fn example4(){
let f = File::open("hello.txt").expect(&String::from("无法打开文件"));
}
传播错误
let res = example5();
use std::io::Read;
use std::io;
fn example5() -> Result<String,io::Error>{
let f = File::open("hello.txt");
let mut f = match f {
Ok(f) =>f,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_)=>Ok(s),
Err(e)=>Err(e),
}
}
?错误传播快捷方式
fn example6() -> Result<String,io::Error>{
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
?与from函数,可以有错误转换。
优化
fn example7()-> Result<String,io::Error>{
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
什么时候用panic
原则
- 在定义一个可能失败的函数是优先考虑Result
- 否则panic
场景
- 演示概念
- 原型代码:unwrap、expect
- 测试
一定是ok时可以使用unwrap
- 传入无意义参数时
- 调用外部不可控代码
- 可预期的用Result
- 值验证
泛型,Trait,生命周期
提取函数消除重复代码
泛型
提高复用能力
相对于模板
泛型函数
fn largest<T>(list:&[T])->T{
let mut largest = list[0];
for &item in list{
if item > largest{ // todo
largest = item;
}
}
largest
}
struct泛型
struct Point<T>{
x:T,
y:T,
}
fn main() {
println!("Hello, world!");
let int_type = Point{x:3,y:4};
let float_type = Point{x:4.1,y:4.5};
}
单态化
Trait
零成本抽象。
抽象的定义共享的行为
类似接口。
定义
pub trait Summary {
fn summarize(&self)->String;
}
实现
pub struct NewsArticle{
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
impl Summary for NewsArticle {
fn summarize(&self)->String{
format!("{},by {} ({})",self.headline,self.author,self.location)
}
}
默认实现
pub trait Summary {
// fn summarize(&self)->String;
fn summarize(&self)->String{
String::from("Read more ..")
}
}
作为参数
pub fn notify(item : impl Summary) {
print!("news:{}",item.summarize());
}
pub fn notify1<T:impl Summary + Display>(item:T){
print!("news:{}",item.summarize());
}
pub fn notify2<T,U>(a:T,b:U) -> String
where
T:Summary+Display,
U:Clone+Debug,
{
print!("news:{}",a.summarize());
}
作为返回
pub fn notify(s:&str)->impl Summary{
NewsArticle{
headline: String::from("震惊!一男子竟独自玩恐怖游戏"),
location: String::from("四川"),
author: String::from("张三"),
content: String::from("ssssss"),
};
}
fn largest<T:PartialOrd + Copy>(list:&[T])->T{
let mut largest = list[0];
for &item in list{
if item > largest{ // todo
largest = item;
}
}
largest
}
生命周期
- 每个引用都有生命周期
- 大多数情况是隐式的,可被推断的
- 手动标志
借用检查器
标注
不影响原生命周期
fn main() {
let str1 = String::from("adbcd");
let str2 = "123456";
println!( "{}",longest(str1.as_str(), str2));
}
fn longest<'a>(x:&'a str,y:&'a str)->&'a str{
if x.len() >y.len(){
x
}else {
y
}
}
测试
测试是一个函数,另开一个线程,如果没有panic就通过
3A操作:
- 准备数据
- 运行代码
- 断言结果
测试函数
加上标志
cargo test进行测试
创建
cargo new add –lib
代码
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}
运行
cargo test
失败测试样例
#[test]
fn another(){
panic!("eeeeerror");
}
断言
assert!判断状态是否为空
#[test]
fn is_true(){
assert!(ret());
}
assert_eq!
assert_nq!
#[test]
fn test_add_two(){
assert_eq!(5,add_two(4));
}
should_panic
发送恐慌通过测试
#[test]
#[should_panic(expected = "aa")]
fn another(){
panic!("aba");
}
测试使用Result
#[test]
fn equal()->Result<(),String>{
if 2+2 == 5{
Ok(())
}
else {
Err(String::from("error"))
}
}
test-thread
显示输出
失败println会打印,否则不会。
可以使用
--show-output
按名称运行测试
cargo test add_two
cargo test add
忽略测试
#[ingore]
cargo test --ingored
测试分类
- 单元测试
- 集成测试
单元测试
#[cfg(test)]
mod tests {
}
测试私有函数
引入
use super::*
集成测试
创建tests目录
目录下的文件被特殊对待。
use adder;
#[test]
fn it_add_two(){
assert_eq!(4,adder::add_two(3))
}
其中的帮助函数
创建一个common文件夹
文件夹下面方帮助函数。
实例-命令行程序
grep工具
接受参数
use std::env; //collect
fn main() {
let args: Vec<String> = env::args().collect();
let query = &args[1];
let filename = &args[2];
println!("worlds: {:?}",query);
println!("file: {:?}",filename);
}
读取文件
use std::env; //collect
use std::fs;
fn main() {
let args: Vec<String> = env::args().collect();
let query = &args[1];
let filename = &args[2];
println!("worlds: {:?}",query);
println!("file: {:?}",filename);
let contents = fs::read_to_string(filename).expect("Something went wrong reading the file");
println!("{}",contents);
}
重构:改进模块和错误处理
use std::env; //collect
use std::fs;
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::new(&args);
let contents = fs::read_to_string(config.filename).expect("Something went wrong reading the file");
println!("{}",contents);
}
struct Config{
query:String,
filename:String,
}
impl Config {
fn new(args:&[String]) -> Config{
let query = args[1].clone();
let filename = args[2].clone();
Config{query,filename}
}
}
创建lib.rs
use std::fs;
use std::error::Error;
pub struct Config{
query:String,
filename:String,
}
impl Config {
pub fn new(args:&[String]) -> Result<Config,&'static str>{
if args.len() < 3{
return Err("not enough aruguments");
}
let query = args[1].clone();
let filename = args[2].clone();
Ok(Config{query,filename})
}
}
pub fn run(config:Config) -> Result<(),Box<dyn Error>>{
let contents = fs::read_to_string(config.filename)?;
println!("file contents {}",contents);
Ok(())
}
main.rs
use std::env; //collect
use std::process;
use grep_project;
fn main() {
let args: Vec<String> = env::args().collect();
let config = grep_project::Config::new(&args).unwrap_or_else(|error|{
println!("Problem parsing arguments:{}",error);
process::exit(1);
});
if let Err(e) = grep_project::run(config){
println!("Application error:{}",e);
process::exit(1);
}
}
使用TDD开发库功能
Test Driven Development
use std::fs;
use std::error::Error;
pub struct Config{
query:String,
filename:String,
}
impl Config {
pub fn new(args:&[String]) -> Result<Config,&'static str>{
if args.len() < 3{
return Err("not enough aruguments");
}
let query = args[1].clone();
let filename = args[2].clone();
Ok(Config{query,filename})
}
}
pub fn run(config:Config) -> Result<(),Box<dyn Error>>{
let contents = fs::read_to_string(config.filename)?;
for line in search(&config.query, &contents){
println!("{}",line);
}
Ok(())
}
pub fn search<'a>(query:&str,contents:&'a str)->Vec<&'a str>{
let mut results = Vec::new();
for line in contents.lines(){
if line.contains(query){
results.push(line);
}
}
results
}
#[cfg(test)]
mod tests{
use super::*;
#[test]
fn on_result(){
let query = "duct";
let contents = "\
Rust:
safe,fast,productive.
Pick three";
assert_eq!(vec!["safe,fast,productive."],search(query,contents));
}
}
使用环境变量
use std::fs;
use std::error::Error;
use std::env; //collect
pub struct Config{
pub query:String,
pub filename:String,
pub case_sensitive:bool,
}
impl Config {
pub fn new(args:&[String]) -> Result<Config,&'static str>{
if args.len() < 3{
return Err("not enough aruguments");
}
let query = args[1].clone();
let filename = args[2].clone();
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
Ok(Config{query,filename,case_sensitive})
}
}
pub fn run(config:Config) -> Result<(),Box<dyn Error>>{
let contents = fs::read_to_string(config.filename)?;
if config.case_sensitive{
for line in search(&config.query, &contents){
println!("{}",line);
}
}else {
for line in search_case_insensitive(&config.query, &contents){
println!("{}",line);
}
}
Ok(())
}
pub fn search<'a>(query:&str,contents:&'a str)->Vec<&'a str>{
let mut results = Vec::new();
for line in contents.lines(){
if line.contains(query){
results.push(line);
}
}
results
}
pub fn search_case_insensitive<'a>(query:&str,contents:&'a str)->Vec<&'a str>{
let mut results = Vec::new();
let query = query.to_lowercase();
for line in contents.lines(){
if line.to_lowercase().contains(&query){
results.push(line);
}
}
results
}
#[cfg(test)]
mod tests{
use super::*;
#[test]
fn case_sensitive(){
let query = "duct";
let contents = "\
Rust:
safe,fast,productive.
Pick three
Duck tape"
;
assert_eq!(vec!["safe,fast,productive."],search(query,contents));
}
#[test]
fn case_insensitive(){
let query = "rUsT";
let contents = "\
Rust:
safe,fast,productive.
Pick three
Duck tape
Trust me"
;
assert_eq!(vec!["Rust:","Trust me"],search_case_insensitive(query,contents));
}
}
将错误消息写入标准错误
use std::env; //collect
use std::process;
use grep_project;
fn main() {
let args: Vec<String> = env::args().collect();
let config = grep_project::Config::new(&args).unwrap_or_else(|error|{
eprintln!("Problem parsing arguments:{}",error);
process::exit(1);
});
if let Err(e) = grep_project::run(config){
eprintln!("Application error:{}",e);
process::exit(1);
}
}
闭包
也叫匿名函数;
可以作为返回值、参数;
可以捕获环境变量;
可以推断输入类型,输出类型;
use std::thread;
use std::time::Duration;
fn main() {
println!("Hello, world!");
get_schdule(20);
}
fn get_schdule(time:u32){
let compute = |args:u32|{
println!("slowly ....");
thread::sleep(Duration::from_secs(2));
args
};
if time>10{
println!("done .. {}",compute(time));
}else {
println!("done .. {}",compute(time));
}
}
类型推断。
结构体闭包
Fn Trait由标准库提供
所有闭包得实现以下之一:
- Fn
- FnMut
- FnOnce
use std::thread;
use std::time::Duration;
fn main() {
println!("Hello, world!");
get_schdule(20);
}
struct Cacher<T>
where T:Fn(i32)->i32
{
calculation:T,
value:Option<i32>,
}
impl <T> Cacher<T>
where T:Fn(i32)->i32
{
fn new(calculation:T)->Cacher<T>{
Cacher{
calculation,
value:None,
}
}
fn value(&mut self,arg:i32)->i32{
match self.value {
Some(v) =>v,
None=>{
let v = (self.calculation)(arg);
self.value = Some(v);
v
}
}
}
}
fn get_schdule(time:i32){
let mut compute = Cacher::new(|args:i32|{
println!("slowly ....");
thread::sleep(Duration::from_secs(2));
args
});
if time>10{
println!("done .. {}",compute.value(time));
}else {
println!("done .. {}",compute.value(time));
}
}
Cache限制:只能保留一个值,类型问题
使用哈希map解决;使用模板
use std::thread;
use std::time::Duration;
use std::collections::HashMap;
fn main() {
println!("Hello, world!");
get_schdule(20);
let mut compute = Cacher::new(|args:i32|args);
println!("{}", compute.value(1));
println!("{}", compute.value(2));
}
struct Cacher<T>
where T:Fn(i32)->i32
{
calculation:T,
value:HashMap<Option<i32>,i32>,
}
impl <T> Cacher<T>
where T:Fn(i32)->i32
{
fn new(calculation:T)->Cacher<T>{
Cacher{
calculation,
value:HashMap::new(),
}
}
fn value(&mut self,arg:i32)->i32{
match self.value.get(&Some(arg)){
Some(v) => *v,
None =>{
let v = (self.calculation)(arg);
self.value.insert(Some(arg), v);
v
}
}
}
}
环境捕获
函数不能捕获环境,捕获需要消耗内存。
-
取得所有权:FnOnce
-
取得可变借用:FnMute
-
不可变借用:Fn
使用move可以移动所有权,多用于多线程
filter
过滤条件为false的值。
保留条件为true的值。
#[derive(Debug)]
struct Shoe{
size:i32,
style:String,
}
fn shoes_in_my_szie(shoes:Vec<Shoe>,shoe_size:i32)->Vec<Shoe>{
shoes.into_iter().filter(|x| x.size == shoe_size).collect()
}
fn test(){
let v = vec![
Shoe{
size:10,
style:String::from("sport"),
},
Shoe{
size:12,
style:String::from("releax"),
},
Shoe{
size:10,
style:String::from("clap"),
},
];
let in_my_size = shoes_in_my_szie(v,10);
for e in in_my_size.iter(){
println!("{:#?}",e);
}
}
迭代器
- 惰性的
let v1 = vec![1,2,3];
let v1_iter = v1.iter();
for val in v1_iter{
println!("{}",val);
}
Iterator trait
所有迭代器都实现了 Iterator trait
要有:
- 返回,item
- 实现,next->元素的不可变引用
into_iter,取得所有权
iter_mut,可变引用
迭代器的产生与消费
必须实现next,是消耗型的。例如:sum
迭代器适配器,是生产型的。例如map
自定义
struct Counter{
count:u32,
}
impl Counter {
fn new()->Counter{
Counter{
count:0
}
}
}
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) ->Option<Self::Item>{
if self.count < 5{
self.count +=1;
Some(self.count)
}else {
None
}
}
}
fn test_diy(){
let mut counter = Counter::new();
for i in (0..6){
println!("{:?}",counter.next());
}
}
循环与迭代器
迭代器会更快一点。
不产生额外开销。
发布
智能指针
指针:指向变量在内存中的地址
Rust中常用的指针是引用
- 使用&
- 借用它指向的值
- 没有额外开销
引用计数智能指针
- 记录引用者的数量,让一份数据多个人同时持有
- 没有使用者自动清理。
String和Vec
使用结构体实现,
Deraf
Drop
Box
实现了
Deraf
Drop
允许在heap上存储数据
在Stack有一点内存,指向heap
可使用与递归
无限大小
use crate::List::{Cons,Nil};
enum List {
Cons(i32,List),
Nil,
}
fn box_test(){
let b = Box::new(5);
println!("{}",b);
let list = Cons(1,Cons(2,Cons(3,Nil)));
}
Box解决
use crate::List::{Cons,Nil};
enum List {
Cons(i32,Box<List>),
Nil,
}
fn box_test(){
let b = Box::new(5);
println!("{}",b);
let list = Cons(1,Box::new(Cons(2,
Box::new(Cons(3,Box::new(Nil))))));
}
Deraf
fn test_drefe(){
let x = 4;
let y = Box::new(x);
assert_eq!(4,x);
assert_eq!(4,*y);
}
use std::ops::Deref;
struct MyBox<T>(T);
impl <T> MyBox<T> {
fn new(x:T)->MyBox<T>{
MyBox(x)
}
}
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self)->&T{
&self.0
}
}
fn test_drefe(){
let x = 4;
let y = Box::new(x);
let y = MyBox::new(x);
assert_eq!(4,x);
assert_eq!(4,*y);
}
Drop
网络,文件资源释放
不能手动调用
struct CustomSmartPointer{
data:String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self){
println!("Droping data: {}",self.data);
}
}
fn test_drop(){
let c = CustomSmartPointer{
data:String::from("hello"),
};
println!("CustomSmartPointer constructed");
}
可以用标准库的drop,来丢弃。
drop(c);
Rc
引用计数智能指针
- 多重所有权
- 单线程
- 不在预导入
- Rc::clone只拷贝引用,不克隆数据
- 不可变引用
use std::rc::Rc;
enum List {
Cons(i32,Rc<List>),
Nil,
}
fn test_rc(){
let a = Rc::new(Cons(5,
Rc::new(Cons(10,
Rc::new(Nil)))));
println!("count after creating {}",Rc::strong_count(&a));
{
let b = Cons(3,Rc::clone(&a));
println!("count after creating {}",Rc::strong_count(&a));
}
let c = Cons(4,Rc::clone(&a));
println!("count after creating {}",Rc::strong_count(&a));
}
RefCell
- 唯一所有权。
- 可变、不可变借用,在运行时检查。
weak防止内存泄漏
并发
Concurrent:程序的不同部分之间独立的运行
Parallel:程序的不同部分同时运行
Rust无畏并发
多线程
问题
- 竞争状态
- 死锁
使用OS,1:1模型,运行时小
语言自己的线程,M:N模型,运行时大
Rust权衡运行时的支持,标准库只提供了1:1模型。
使用spawn创建线程
主线程结束,另一个线程也结束。提供joinhandle等待线程结束才结束。
use std::thread;
use std::time::Duration;
fn main() {
println!("Hello, world!");
let handle = thread::spawn(||{
for i in 1..10{
println!("h1, number {} from the spawned thread!",i);
thread::sleep(Duration::from_millis(1))
}
});
// handle.join().unwrap();
for i in 1..5{
println!("h1, number {} from the main thread",i);
thread::sleep(Duration::from_millis(1))
}
handle.join().unwrap();
}
move闭包
可以使用其他线程的数据。
fn spawn_move(){
let v = vec![1,2,4];
let handle = thread::spawn(move||{
println!("here's a vector {:?}",v);
});
handle.join().unwrap();
}
消息传递
Channel
包含:发送端,接受端
创建
mpsc(多生产者,单消费者)
mpsc::channel
use std::sync::mpsc;
use std::thread;
fn create_channel(){
let (tx,rx) = mpsc::channel();
thread::spawn(move ||{
let val = "hi".to_string();
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();//block
print!("Got {}",received);
}
try_recv非阻塞。
所有权
- send后所有权就移动了。
克隆创建多个发送者
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn clone_sender(){
let (tx,rx) = mpsc::channel();
let tx1 = mpsc::Sender::clone(&tx);
thread::spawn(move ||{
let vals = vec![
String::from("1: hi"),
String::from("1: from"),
String::from("1: the"),
String::from("1: thread"),
];
for val in vals{
tx1.send(val).unwrap();
thread::sleep(Duration::from_millis(100));
}
});
thread::spawn(move ||{
let vals = vec![
String::from("2: hi"),
String::from("2: from"),
String::from("2: the"),
String::from("2: thread"),
];
for val in vals{
tx.send(val).unwrap();
thread::sleep(Duration::from_millis(100));
}
});
for received in rx{
println!("Got: {}",received);
}
}
共享内存并发
channel是单所有权,共享是多所有权
Mutex只允许一个线程来访问某些数据。
通过lock,和unlock来使用
use std::sync::Mutex;
fn create_mutex(){
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}//unlock
println!("m = {:?}",m);
}
多线程多所有权
Rc要实现send
Arc,原子Rc
use std::sync::{Mutex,Arc};
use std::rc::Rc;
fn create_mutex(){
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}//unlock
println!("m = {:?}",m);
}
fn mutex_thread(){
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10{
let counter = Arc::clone(&counter);
let handle = std::thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles{
handle.join().unwrap();
}
println!("Result {}",*counter.lock().unwrap());
}
Send Sync trait
send 转移所有权。
几乎所有类型都实现了send
Sync允许多个线程访问
Rc,未实现,Mutex实现了。
面向对象
封装、继承、多态
Rust,封装;没有继承,trait可以复用;多态,泛型和trait约束;
实现面向对象的设计模式
状态模式
pub struct Post{
state:Option<Box<dyn State>>,
content:String,
}
impl Post {
pub fn new()->Post{
Post{
state:Some(Box::new(Draft {})),
content:String::new(),
}
}
pub fn add_text(&mut self,text:&str){
self.content.push_str(text)
}
pub fn content(&self) ->&str{
self.state.as_ref().unwrap().content(&self)
}
pub fn request_review(&mut self){
if let Some(s) = self.state.take(){
self.state = Some(s.request_review())
}
}
pub fn approve(&mut self){
if let Some(s) = self.state.take(){
self.state = Some(s.approve())
}
}
}
trait State {
fn request_review(self:Box<Self>) ->Box<dyn State>;
fn approve(self:Box<Self>) ->Box<dyn State>;
fn content<'a>(&self,post:&'a Post) -> &'a str{
""
}
}
struct Draft {}
impl State for Draft {
fn request_review(self: Box<Self>) ->Box<dyn State>{
Box::new(PendingReview {})
}
fn approve(self: Box<Self>)->Box<dyn State>{
self
}
}
struct PendingReview {}
impl State for PendingReview {
fn request_review(self: Box<Self>) ->Box<dyn State>{
self
}
fn approve(self: Box<Self>)->Box<dyn State>{
Box::new(Published{})
}
}
struct Published {}
impl State for Published {
fn request_review(self: Box<Self>) ->Box<dyn State>{
self
}
fn approve(self: Box<Self>)->Box<dyn State>{
self
}
fn content<'a>(&self,post:&'a Post) -> &'a str{
&post.content
}
}
use Blog::Post;
fn main() {
println!("Hello, world!");
let mut post = Post::new();
post.add_text("I ate a salad for lunch today");
post.request_review();
post.approve();
}
模式
模式匹配
有,字面值,数值、结构体、枚举、变量、通配符、占位符
使用模式
- match
- if let
- while let
- for
- let
- 函数参数
可辩驳型
无可辩驳的,let x = 5;
可辩驳的 , if let some(v) = a_value;
- 忽略
_x 不会警告
_ 不会绑定
{x,..}
(x,..,y)
match + if
match v{
4|5|6|7|8 if ..
}
高级特效
不安全Rust
不保证内存安全
- 解引用原始指针
- 调用unsafe函数或方法
- 访问或修改可变静态变量
- 实现unsafe trait
unsafe仍会检查借用
解引用原始指针
与c交换
*mut T
*const T,这里是类型名的一部分
fn test(){
let mut num = 5;
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
unsafe{
println!("r1:{}",*r1);
println!("r2:{}",*r2);
}
let address = 0x012345usize;
let r = address as *const i32;
unsafe{
println!("r:{}",*r);
}
}
高级Trait
pub trait Iterator {
type Item;
fn next(&mut self) ->Option<Self::Item>;
}
使用不同的参数可以为一个struct多次实现
重载
#[derive(Debug)]
struct Point{
x:i32,
y:i32,
}
use std::ops::Add;
impl Add for Point {
type Output = Point;
fn add(self,other:Point)->Point{
Point{
x:self.x + other.x,
y:self.y + other.y,
}
}
}
fn test_ops(){
let p1 = Point{
x:1,
y:2
};
let p2 = Point{
x:2,
y:3,
};
println!("{:?}",p1+p2);
}
完全限定语法
<Type as Trait>::function(receiver_if_method,next_arg,..);
类型别名
type elem = i32;//此后elem表示i32类型
函数指针
Fn,FnMut,FnOnce
宏
宏是元编程。
声明宏,快弃用了
过程宏,
- 自定义派生
- 属性宏
- 函数宏
结课项目
单线程
use std::net::{TcpListener,TcpStream};
use std::io::{Read,Write};
use std::fs;
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming(){
let stream = stream.unwrap();
println!("Connection established!");
handle_connection(stream);
}
}
fn handle_connection(mut stream:TcpStream){
let mut buffer = [0;512];
stream.read(&mut buffer).unwrap();
// println!("Request: {}",String::from_utf8_lossy(&buffer[..]));
let get = b"GET / HTTP/1.1\r\n";
let (status_line,filename) = if buffer.starts_with(get){
("HTTP/1.1 200 OK\r\n\r\n","hello.html")
}else {
("HTTP/1.1 404 NOT FOUND\r\n\r\n","404.html")
};
let response = format!("{}{}",status_line,fs::read_to_string(filename).unwrap());
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
完整
use std::net::{TcpListener,TcpStream};
use std::io::{Read,Write};
use std::fs;
use std::io::prelude::*;
use std::thread;
use std::time::Duration;
use server::ThreadPool;
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
let pool = ThreadPool::new(4);
for stream in listener.incoming().take(2){
let stream = stream.unwrap();
pool.excute(||{
handle_connection(stream);
});
}
}
fn handle_connection(mut stream:TcpStream){
let mut buffer = [0;512];
stream.read(&mut buffer).unwrap();
// println!("Request: {}",String::from_utf8_lossy(&buffer[..]));
let get = b"GET / HTTP/1.1\r\n";
let (status_line,filename) = if buffer.starts_with(get){
("HTTP/1.1 200 OK\r\n\r\n","hello.html")
}else {
("HTTP/1.1 404 NOT FOUND\r\n\r\n","404.html")
};
let response = format!("{}{}",status_line,fs::read_to_string(filename).unwrap());
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
use std::thread;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
enum Message {
NewJob(Job),
Terminate,
}
pub struct ThreadPool{
workers:Vec<Worker>,
sender:mpsc::Sender<Message>,
}
impl ThreadPool {
pub fn new(size:usize)->ThreadPool{
assert!(size>0);
let (sender,receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size{
workers.push(Worker::new(id,Arc::clone(&receiver)));
}
ThreadPool {workers,sender}
}
pub fn excute<F>(&self,f:F)
where
F:FnOnce() + Send +'static,
{
let job = Box::new(f);
self.sender.send(Message::NewJob(job)).unwrap();
}
}
impl Drop for ThreadPool {
fn drop(&mut self){
println!("Sending terminate message to all workers.");
for _ in &mut self.workers{
self.sender.send(Message::Terminate).unwrap();
}
println!("Shutting down all workers.");
for worker in &mut self.workers{
println!("Shutting down worker {}",worker.id);
if let Some(thread) = worker.thread.take(){
thread.join().unwrap();
}
}
}
}
// struct Job;
type Job = Box<dyn FnBox + Send + 'static>;
struct Worker{
id:usize,
thread:Option<thread::JoinHandle<()>>,
}
trait FnBox {
fn call_box(self:Box<Self>);
}
impl <F: FnOnce()>FnBox for F {
fn call_box(self: Box<Self>){
(*self)();
}
}
impl Worker {
fn new(id:usize,receiver:Arc<Mutex<mpsc::Receiver<Message>>>) ->Worker{
let thread = thread::spawn(move || loop{
let message = receiver.lock().unwrap().recv().unwrap();
match message{
Message::NewJob(job) =>{
println!("Worker {} got a job;executing",id);
job.call_box();
}
Message::Terminate =>{
println!("Worker {} was told to terminate",id);
break;
}
};
});
Worker{id,thread:Some(thread)}
}
}
构建脚本
有c库,链接c库等,生成rust模块
-
添加依赖
[package] name = "script_learning" version = "0.1.0" edition = "2021" build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies]
-
编写build.rs
use std::env; use std::fs::File; use std::io::Write; use std::path::Path; fn main(){ let out_dir = env::var("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("hello.rs"); let mut f = File::create(&dest_path).unwrap(); f.write_all(b" pub fn say_hello() -> &'static str{ \"hello\" } ").unwrap(); }
-
调用
include!{concat!(env!("OUT_DIR"),"/hello.rs")} fn main() { println!("{}",say_hello()); }
外部
-
依赖
[build-dependencies] cc = '1.0'
-
build.rs
extern crate cc; fn main(){ cc::Build::new().file("src/hello.c").compile("hello"); }
-
hello.c
#include<stdio.h> void hello(){ printf("hello world 11\n"); }
-
main.rs
// include!{concat!(env!("OUT_DIR"),"/hello.rs")} extern {fn hello();} fn main() { // println!("{}",say_hello()); unsafe{ hello(); } }