目录c++
rust是什么?rust是一门新型的编程语言,目的是取代c/c++作为一个系统开发级别的编程语言。它有着c的底层高效,有着c++的现代描述能力,同时保持足够的洁简。做为一个新语言,他在吸收现代语言精华的同时,也避免其中设计中的一些繁琐之处,同时它已经被许多大型的开发项目所采用,有着良好的商业化前景。express
rust属于静态类型语言。即编译时必须知道全部变量的类型。编程
let 模式:数据类型 = 表达式; //定义不变量 let mut 模式:数据类型 = 表达式; //定义变量
长度 | 有符号 | 无符号 |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
架构 | isize | usize |
长度 | 名称 |
---|---|
32-bit | f32 |
64-bit | f64 |
采用IEEE-754标准表示。数组
类型 | 真值 | 假值 |
---|---|---|
bool | true | false |
类型 | 值样例 |
---|---|
char | 'A' |
字符是unicode标准编码的标量值。安全
将多个数据类型组合成一个新的类型。bash
类型样例 | 值样例 |
---|---|
(i32,char,u8) | (500,'B',1) |
模式解构元组:数据结构
let (x,y,z)=(1,'A',3);
多线程
类型样例 | 值样例 |
---|---|
[i64;5] | [1,2,3,4,5] |
数组是固定长度,且元素类型相同。闭包
类型样例 | 值样例 |
---|---|
struct User{字段序列} | User{键值对序列} |
struct User{age:u8,active:bool} | User{age:18,active:true} |
struct User(u8,bool); | User(18,true) |
struct User; | User |
类型样例 | 值样例 |
---|---|
enum 名称{类结构序列} | enum Message{Quit,Move{x:u8,y:u8},Write(u8,u8)} |
对数组的某个片断创建投影视图的类型。切片是动态大小类型,所以只能使用切片引用。架构
类型样例 | 值样例 |
---|---|
str | let a:&str = &"hello"[2..4]; |
[T] | let a:&[i8] = &[1,2,3][0..3]; |
引用是指针(保存地址),指向具体数据。在rust中,引用是没有数据全部权,而只是”借用“使用权的特殊类型。
类型样例 | 值样例 |
---|---|
&类型 | let i :&int= &3; let j :int=*i; |
智能指针指的是能自动释放相关资源的一系列数据结构。它表现形式相似指针,但大多数状况都拥有数据全部权,所以概念上和rust引用不相干。
常见智能指针:
用途:嵌套类型、大数据转移全部权、特征接口。
智能指针通常须要实现:解引用特征和析构特征。
解引用特征:
use std::ops::Deref;//解不可变引用 // DerefMut //解可变引用 struct MyBox<T>(T); impl<T> Deref for MyBox<T>{ type Target = T; fn deref(&self)->&T{ &self.0 } } let a = Mybox(3); let b = *a;// *a = *(a.deref()) = *(&T) = 3 //传参时会自动解引用强制多态deref coercions。 fn f(x:&i32){println!("x={}",*x);} f(&a); // &a = &(*a) = &(3)
析构特征:
impl<T> Drop for MyBox<T>{ fn drop(&mut self){ println!("自动释放资源。"); } } fn main(){ let c = MyBox(3); drop(c);//手动释放 }
rust经过引用来实现move的补充“借用”,同时用规则和编译检查来保证引用的安全性。可是某些时候仍是须要相似c语言的原生指针。
let mut a=3; let b = &a as *const i32; //指向不可变数据 let c = &mut a as *mut i32;//指向可变数据 let d = 0x234usize; let e = d as *const i32;//指向指定地址。 unsafe{//只能在不安全块中解引用,获得指向的数据 println!("*b={},*e={}", *b, *e); }
fn fa(f:fn(i32)->i32, a:i32)->i32{ f(a) + f(a) //经过函数指针f调用函数 } fn fb(x:i32)->i32{ x + x } let a = fa(fb, 5);//a=20;
rust 特殊用途的类型。
//别名类型。可命名的等价类型 type Myint = i32; //从不返回类型never type:"!" fn bar() ->!{ loop{} } //动态大小类型:str、特征 let s:str = "hi"; //error.没法肯定str类型静态大小 let s:&str = "hi"; //&str 由指针和长度组成,大小等于 usize*2 let s:&std::fmt::Display = &"hi"; //str实现了Display特征,而特征是动态大小,因此只能创建特征的引用 //用于肯定范型参数大小的特征:sized fn f<T:sized>(x:T){} //sized说明T是有静态大小的,sized可省略,自动添加 fn f2<T:?sized>(x:&T){}//?sized表示sized可选,于是x只能是引用类型&T。
rust经过模式匹配来实现灵活的判断和定义变量。
模式种类:
let a=3; //模式a必然匹配 if let Some(b)=None{}; //模式Some(b)能够不匹配 match a{ 1 => ,//字面值模式 1 | 2 => , //1 或 2 4...9 => , //4 到 9 3 if 3<2 =>, //匹配守卫,添加更多判断 _ => ,//必然匹配占位符 } //对复合结构的解构匹配 struct Point{ x:i32, y:i32, } let c = Point{x:1,y:2}; let Point{x,y} = c; //解构出x=1, y=2两个变量 let &Point{x:x2,y:y2}= &c;//对引用进行解构,获得x2,y2 let (_,d,..)=(1,2,3,4,5); //解构元组,忽略第一个值,得d=2,忽略剩余值 if let e@1...5 = d {println!("e={}",e)};//@绑定匹配的值
格式 | 范例 |
---|---|
fn 函数名(参数列表)->返回类型{函数体} | fn main(){} |
函数头部由函数名、参数列表和返回类型组成,参数列表是由逗号分隔的一系列变量组成;函数体由一系列语句(statements)和一个可选的表达式(expressions)结尾组成。
语句没有返回值,以分号结尾。表达式有返回值,没有分号结尾。
格式 | 范例 |
---|---|
if 布尔表达式 {真值语句体} else {假值语句体} | if 5>4 {} else {} |
if分支结构只会执行其中一条分支。if 分支结构是一个表达式。
格式 | 范例 |
---|---|
match 变量{模式=>返回值,...} | match m{Message::Quit=>1,Message::Write(x,y)=>x+y,Message::Move=>0} |
模式匹配必须穷尽全部状况。“_”下划线做为模式表示匹配全部。
格式 | 范例 |
---|---|
if let 模式=变量{...} | if Message::Quit=m{1} |
if let匹配指定模式,忽略其余状况。
格式 | 范例 |
---|---|
loop{循环语句体} | loop{} |
while 布尔表达式{循环语句体} | while 5>4 {} |
while let 模式{循环体} | while let Some(t)= s.pop(){} |
for 元素 in 集合{循环语句体} | for i in [1,2].iter(){} |
impl块能够将函数或方法关联到结构struct、枚举enum。
格式 | 范例 |
---|---|
impl 结构名{函数列表} | impl User{fn hello(&self){}} |
取代特定类型的占位符。经过特征trait描述占位符具有的通用能力。
rust范型支持特例化技术。即对特定类型能定义不一样的操做逻辑。
范型最终会单态化monomorphization,即编译为特定类型的代码,所以不会有性能的开销,但会产生多份代码的数据占用。
格式 | 范例 |
---|---|
基础结构 名称 <占位符列表> ... | fn f<T,U>(x:T,y:U){} |
trait Add<RHS=Self>{ //RHS默认类型是对象类型自身 type Output; fn add(self,rhs:RHS)->Self::Output; } struct A(i32); impl Add for A{ type Output = i32; fn add(self, r:A)->i32{self.0 + r.0} } let a = A(3); let b = A(4); println!("a+b={}", a.add(b));//a+b=7;
trait 相似接口。
特征定义:
格式 | 范例 |
---|---|
trait 名称{声明序列} | trait s{fn t();} |
实现特征:
格式 | 范例 |
---|---|
impl 特征 for 结构{实现序列} | impl s for m{fn t(){}} |
变量 :impl 特征 | fn f(t :impl s){} |
范型占位符 : 特征 | fn f
|
同上 | fn f
|
关联类型只容许一个实现,而范型能够有n个实现。
trait IA{ type Item;//关联类型 fn print(&self)->Self::Item; } struct A; impl IA for A{ type Item = i32; fn print(&self)->Self::Item{3} } let a = A; let b = a.print(); //b = 3i32
特征对象是rust面向对象技术的基础之一,实现同一接口多态调用。
特征对象要求:
格式 | 范例 |
---|---|
&特征 | let a:&Drop = &"3".to_string(); |
闭包是能够存储到变量,捕获调用者做用域的值的匿名函数特征。
格式 | 范例 |
---|---|
|参数|{} |
let expr = |n|{n*2}; |
fn fa()=>Box<Fn()->i32>{ let a = 3;//局部变量 Box::new(move||a) //闭包捕获a,但fa返回闭包,所以闭包生命周期比fa更长,默认捕获的a无效(经过引用),move关键字将a全部权转移到闭包。 //Fn闭包是一种特征,所以无大小,需用智能指针Box包装 } let a = fa()(); //a=3
trait Iterator{ type Item; fn next(&mut self) ->Option<Self::Item>; }
迭代器 | 做用 |
---|---|
into_iter() | 全部权迭代器 |
iter() | 可变引用迭代器,引用元素 |
iter_mut() | 可变引用迭代器,可变引用元素 |
rust语言为了高效管理内存,使用全部权概念取代垃圾回收机制和手工管理内存。
栈stack和堆heap是内存的两个区域。栈存放做用域上下文的变量,而堆存放自由分配的变量(经过指针使用)。
rust的堆中变量赋值是移动move语义,而非浅拷贝shallow copy或深拷贝deep copy。
所以全部权的意义是:值的全部者只有一个。该全部者离开做用域即丢弃值。
引用类型对move语义进行了补充,它不得到全部权,而只有使用权。为了保证数据有效性,编译器对引用对象的生命周期进行严格的检查和假定,默认函数没法经过引用返回对象。
关于引用(&类型):
获取值的三种方式:
方式 | 全部权 | 生命周期 | 相关特征 | 关键字 |
---|---|---|---|---|
T | 转移 | 自动 | FnOnce | move |
&T | 无 | 编译检查 | Fn | |
&mut T | 无 | 编译检查 | FnMut |
变量都有生命周期,按定义的顺序在做用域末尾逆序终结。引用自身的生命周期必须短于所引用对象的生命周期,不然出现悬垂引用(指向无效对象的指针)。
默认在同一个函数内能够经过借用检查器borrow checker自动判断,可是跨函数传递引用就没法自动处理。由于函数可被任意上下文调用,因此没法假定引用参数应该具有怎样的生命周期。
生命周期注解是一种针对引用参数和拥有引用的复合结构与函数的范型占位符。它能够将几个变量的生命周期进行关联,声明具有“合理一致”的生命周期,从而让编译器取得检查引用有效性的必要信息。
所谓“合理一致”指的编译器对关联的参数和返回值作出合理假设。如:
对于结构变量:
格式 | 范例 |
---|---|
&'注解 类型 | fn f<'a>(x:&'a i32){} |
struct 结构<'占位符>{引用成员类型注解} | struct s<'a>{p: &'a str,} |
fn 函数(注解参数列表)->注解返回类型{} | fn f<'a>(x:&'a){} |
程序生命周期:&'static | let s:&'static str="str"; |
let a = 3; let mut p =&a; let mut p2; let b = 4; p = &b; //error,p比b长寿,由于b在p以后定义,即b先于p终结 fn f<'a>(x:&'a isize,y:&'a isize)->&'a isize{ x } p2 = f(&a,&b);//error, p2比f(a,b)长寿。由于虽然a比p2长寿,但b比p2短命,所以f(a,b)=b, b<p2,即便实际返回的是a struct C<'a>(mut &'a isize); let mut c = C(&b); let d = 5; c.0 = &d;//error, c.0 < c,被注解字段必须比结构长寿 //生命周期子类型lifetime subtyping struct E<'e, 'c :'e>{c:&'e C<'c>} struct E2<'e, T :'e>{c:&'e T} //相似定义 let e = E2{c:&c}; fn fe(x:C)->&isize{ E{c:&x}.c.0 //ok, 虽然E是临时变量,x是move进来的参数,但E的定义让其拥有两个格外生命周期:E::c 和C::0,其中E自身的生命周期对应临时变量,E::c对应x,而C::0只是被注解省略,对应->& isize }
为了编码方便,如下状况能够省略注解:
现代语言相对传统语言,我的认为最大的便利在于有一个设计良好的包管理系统。如何组织代码,是一门管理学问,将有助咱们软件工程的建设。
./Cargo.toml 工程描述文件,能够是一个项目,也能够是多个项目的工做区。
[package] name="包名" version = "0.1.1"
项目一个到n个crate(箱)组成,一个crate对应一个.rs源文件。
./src/main.rs 项目根,0-n个
./src/lib.rs 项目根(库),0-1个
./src/bin/* 项目根(非默认项目)
库项目能够被其余项目导入使用。
通常同时须要修改工程描述文件,添加依赖:
#Cargo.toml [dependencies] mylib = { path = "../mylib"}
注意,当前rust语言有两个版本,最新版的模块系统已经进行了大改,请务必使用nightly每晚编译版本rust。
crate能够划分模块。一个crate能够有1个到n个模块。默认模块名x对应x.rs,其中根为固定的”crate“名。
例子:
# :: 外部模块前缀 ./src/main.rs #crate 根模块(隐藏) ./src/a.rs #crate::a ./src/b.rs #crate::b ./src/c.rs #crate::c ./src/c/d.rs #crate::c::d
权限规则:
源文件定义:
//main.rs -- crate mod a; //在根层即根同目录找a.rs定义 mod b{ pub fn f2(); } //内联,将隐藏b.rs文件 mod c; fn main(){ a::f(); b::f();//error。f不存在被隐藏 c::f();//error.私有 c::d::f(); //d 必须在c/mod.rs定义为pub mod d; } fn f(){} //a.rs -- crate::a pub fn f(){} fn f2(){} //b.rs -- crate::b use c::d; //use使用的永远是绝对路径,等于crate::c::d,而不是crate::b::c::d。 fn f(){ d::f2(); //ok c::d::f2(); //由于use crate::c::d;,因此导入了指定crate的模块信息到当前模块,因此c::d::f2()不会被识别为crate::b::c::d::f2() } //c.rs -- crate::c pub mod d; fn f(){ d::f2(); //使用相对路径,等于crate::c::d::f2(); self::d::f2(); //等于上面 } //c/d.rs -- crate::c::d pub fn f(){ crate::a::f();//用绝对路径::访问其余模块 crate::a::f2();//error a不是d的父亲,d不能访问其私有成员 crate::b::f2();//访问的是在main.rs中的定义 super::f();//等价crate::c::f(), ok super::super::f();//相对路径访问main.rs 的f() } pub fn f2(){}
运用策略:
Cargo.toml 设置:
[profile.release] panic = 'abort' #直接终止程序 #或者展开回溯unwinding
使用演示:
panic!("error message!");
enum Result<T,E>{ Ok(T), Err(E), } let f = File::open("hello.txt").expect("自动报错。"); //.unwrap();自动报错,使用内部信息 let mut s = String::new(); f.read_to_string(&mut s)?; //?自动向调用方返回错误对象。
let mut v:Vec<i64> = Vec![1,2,3]; v.push(5); let el :i64 = v[1]; let el2 :Option<i64> = v.get(1); for i in &mut v{ *i += 123; }
String
是Vec<u8>
的包装。
let mut s = String::from("hello world."); s.push_str("man."); format!("{}-{}-{}", s,s,s) let c :char = s.chars()[1]; let c2 :u8 = s.bytes()[2]; let c3 :&str = &s[3..];
HashMap<K,V>
use std::colletions::HashMap; let mut m= HashMap::new(); m.insert(String::from("yes"),0); let v :Option<_> = m.get(&String::from("yes"));
基本的堆引用,具备全部权。
let a = Box::new(3);//3存放在堆上,a有全部权 let b:i32 = *a;//解引用,取得堆上值3
引用计数(多全部权)。
use std::rc::Rc; let a = Rc::new(3); let b = Rc::clone(&a);//引用计数+1 let c = Rc::clone(&a);//引用计数2
返回内部可变引用
let a =Rc::new(RefCell::new(3));//建立内部可变3 let b =Rc::clone(&a); let c =Rc::clone(&a); *a.borrow_mut()+=1; //返回内部可变引用
弱引用,没全部权。可用于解决循环引用问题。
let a=Rc::new(3); let b:Weak<_>=Rc::downgrade(&a);//从Rc引用中建立弱引用 drop(a); //手动删除a println!("b={}",b.upgrade().expect("没法访问b"));
//生成自动打印内部信息的特征 #[derive(Debug)] struct A(&str, i8); println!("{:?}", A("hi", 3)); //生成等于、不等特征 #[derive(PartialEq)] //Eq struct B; println!("B==B?{}", B==B); //生成有序特征 #[derive(Ord,PartialOrd,Eq,PartialEq,Debug)] struct C(i8); println!("{:?}>{:?}?{}",C(3),C(1),C(3)>C(1)); //生成克隆特征(要求字段实现克隆特征才有效果) #[derive(Clone)] //Copy struct D(i8); let mut a=D(3); let b = a.clone(); let pa = a.0 as *const i8; let pb = b.0 as *const i8; println!("&{:?}->&{:?}={}",pa,pb,b.0); //生成哈希特征 #[derive(Hash)] struct E; //生成默认值特征 #[derive(Default)] struct F;
并行处理,会面对以下问题:
thread::spawn()
use std::thread; let a = 3; let handle = thread::spawn(move||{ println!("新线程。a={}", a);//move转移a的全部权 }); handle.join().unwrap();//主线程等待新线程执行完毕 //不然主线程执行完毕会自动关闭子线程
新兴的一种协调并发处理的方式。易用,单拥有权。
use std::sync::mpsc;//多产单消multiple producer,single consumer use std::thread; let (tx,rx)=mpsc::channel(); thread::spawn(move ||{tx.send("hi".to_string()).unwrap();}); println!("{}",rx.recv().unwrap());//获取“hi”
互斥器mutex(mutual exclusion):任意时刻,只容许一个线程访问数据。多拥有权。
use std::sync::Mutex; use std::sync::Arc; //原子性Rc use std::thread; let a = Mutex::new(3); { let mut b :MutexGuard<_>= a.lock().unwrap();//获取互斥器内部数据的智能指针 //该指针拥有锁,并能自动释放锁 *b+=1; }//自动释放锁 println!("a={:?}",a);//可再次读取数据 //多线程共享互斥器 let b = Arc::new(Mutex::new(3));//Arc是跨线程安全的Rc,即多全部权智能指针 let c = Arc::clone(&b);//增长计数 let handle1 = thread::spawn(move||{ let n = c.lock().unwrap();//共享同一个锁 *n += 1; }); let d = Arc::clone(&b);//计数3 let handle2 = thread::spawn(move||{ let n = d.lock().unwrap();//共享同一个锁 *n += 2; }); handle1.join().unwrap(); handle2.join().unwrap(); println!("b={}", *b.lock().unwrap());
#[cfg(test)] //只在配置是test编译 mod tests{ use super::*; //导入所有外部模块 #[test] //测试项目 fn exploration()->Result<(),String>{ assert!(3>2); panic!("测试失败。"); Err(String::from("Result失败")) } #[test] #[should_panic] //但愿出现异常,不然不经过 fn g(){ assert!(3<2); } }
构建目录:/tests/,每一个.rs做为独立crate构建。
use adder; //导入待测试模块 #[test] fn g(){ assert!(2>3,"出错"); }
面向对象:封装并自行维护内部状态的结构体,在相同接口下多态调用。
rust具备面向对象的设计能力,但不强制使用面向对象,由于封装是须要代价的。不要在细颗粒度下使用面向对象,避免过分包装。
多态技术 | 动态性 | 同构性 |
---|---|---|
范型 | 静态 | 同构 |
特征对象 | 动态 | 异构 |
面向对象接口设计建议:
一些命令行下的编译管理工具: