通用集合类型 Vec<T>
vector允许在单独数据结构中存储多于一个的值,它们在内存中相邻
排列,vector被丢弃时,其中的数据也会被丢弃
存储不同类型 vector只能存储相同类型的值,因为vector必须在编译前知道所有
存储元素所需内存、允许的元素类型,否则对vector进行操作可能
会出错。但是可以使用枚举类型存储”不同类型”(Message为例)
1 vec! [Message::Write(String ::from("ab" ), Message::Move{x:5 , y:6 }]
常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let mut v:Vec <i32 > = Vec ::new();let mut v = Vec ::with_capacity(3 ); let v = vec! [1 , 2 , 3 ];v.push(5 ) let third:&i32 = &v[2 ] let third:Option <&i32 > = v.get(2 ) let mut v = vec! [100 , 32 , 57 ]for i in &mut v{ *i += 50 ; } let iter = v.iter(); let iter_mut = v.iter_mut(); let iter_owner = v.into_iter();
使用enum+match就能保证处理所有类型,不会出错
字符串 通常意义上的字符串往往是以下两种的”综合“
索引字符串 因此String
类型不能索引获取字符,索引操作预期是常数时间,
而utf-8字列序列并不能保证在常数时间内获取“字符”,rust需要
从头检查。另外,字符串中可能有不可见字符(如发音字符),
即字形簇 和字符串 不等价,此时索引的意义也不明确。
更加有价值的是使用[]
和range创建一个字符串slice需要注意的是
如果字符串slice不是有效处utf-8编码序列,程序会在运行时
panic!
1 2 3 4 5 let len = String ::from("Здравствуйте" ).len() let hello = "Здравствуйте" ;let s = &hello[0 ..4 ];
遍历字符串
返回字符Unicode值char
类型1 2 3 for c in "नमस्ते" .chars() { println! ("{}" , c); }
将会打印出6个字符,两个不可见的发音字符
返回字节byte值u8
类型1 2 3 for b in "नमस्ते" .bytes() { println! ("{}" , b); }
String
常用方法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 let mut s = String ::new();let s = String ::from("initial contet" );let data = "initial content" ;let s = data.to_string();let s = "initial content" .to_string();let mut s = String ::from("foo" );s.push_str("bar" ); let s2 = "bar" ;s.push_str(&s2); println! (s2); s.push('l' ); let s1 = String ::from("hello, " );let s2 = String ::from("world" ;let s3 = s1 + &s2; let s1 = String ::from("tic" );let s2 = String ::from("tac" );let s3 = string::from("toc" );let s = format! ("{}-{}-{}" , s1, s2, s3);
HashMap HashMap键、值必须是同质的,相对来说使用频率较低,没有引入
prelude
,使用之前需要用use
关键字引入
HashMap
默认使用一种密码学安全的哈希函数,它可以抵抗拒绝
服务(Denial of Service, DoS)攻击。然而这并不是可用的最快的
算法,不过为了更高的安全性值得付出一些性能的代价。可指定不同
hasher 来切换为其它函数。hasher 是一个实现了BuildHasher
trait的类型
常用函数 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 32 33 34 35 36 37 use std::collections::HashMap;let mut scores = HashMap::new();scores.insert(String ::from("Blue" ), 10 ); scores.insert(String ::from("Yellow" ), 30 ); scores.insert(String ::from("Blue" , 20 ); scores.entry(String ::from("Yellow" )).or_insert(90 ); scores.entry(String ::from("Red" )).or_insert(90 ); let text = "hello world wonderful word" ;let mut map = HashMap::new();for word in text.split_whitespace(){ let count = map.entry(word).or_insert(0 ); *count += 1 ; } let teams = vec! [String ::from("Blue" ), String ::from("Yello" )];let initial_scores = vec! [10 , 30 ];let scores: HashMap<_,_> = teams.iter.zip(initial_scores.iter()).collect(); let team_name = String ::from("Blue" );let team_score = scores.get(&team_name);for (key, val) in &scores{ println! ("{}:{}" , key, val); }
智能指针
指针pointer:包含内存地址的变量,这个地址引用(指向)
其他数据
智能指针smart pointer:一类数据结构,表现类似于指针,
拥有额外的元数据和功能
Rust中最常见的指针是引用reference ,除了引用数据没有其他
特殊功能,也没有任何额外开销。
智能指针通常由结构体实现,区别于常规结构体的特在于实现了
Deref
和Drop
trait
事实上,String
和Vec<T>
也是智能指针
Deref
trait、DerefMut
trait
Deref
trait:重载解不可变引用 运算符*
DerefMut
trait:重载解可变引用 引用运算符*
允许智能指针结构体实例表现得像引用,可以让代码兼容智能指针
和引用
1 2 3 4 5 6 7 8 fn main (){ let x = 5 ; let y = &x; let z = Box ::new(5 ); assert_eq! (5 , x); assert_eq! (5 , *y); assert_eq! (5 , *z);
自定义类型实现Deref
trait 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 struct MyBox <T>(T); impl <T> MyBox<T>{ fn new (x: T) -> MyBox<T>{ MyBox(X) } } use::std::Deref; impl <T> Deref for MyBox<T>{ type Target = T; fn deref (&self ) -> &T{ &sell.0 } } fn main (){ let x = 5 ; let y = MyBox::new(x); println! ("y = {}" , *y); }
DerefMut
trait类似
隐式解引用强制多态Deref Coercions 将实现了Deref
trait或DerefMut
trait类型的引用转换为其他
类型的引用,通过多次 的隐式 转换使得实参和型参类型
一致,(这些解析发生在编译时,没有运行时损失)避免多次使用
&
和*
引用和解引用,也使得代码更容易兼容智能指针和引用。
1 2 3 4 5 6 7 fn hello (name: &str ){ println! ("hello, {}" , name); } fn main (){ let m = MyBox::new(String ::from("Rust" )); hello(&m); }
实参类型T和型参类型U满足(间接)
T: Deref<Target = U>
:&T
转换为&U
T: Deref<Target = U>
:&mut T
转换为&U
T: DerefMut<Target = U>
:&mut T
转换为&mut U
相当于在引用外面添加任意层&(*_)
、&mut(*_)
,直到实参类型
和型参类型一致
Drop
traitDrop
trait要求实现drop
方法(析构函数destructor),获取
&mut self
可变引用,智能指针离开作用域时运行drop
方法中的
代码,用于释放类似于文件或网络连接的资源,编译器会自动插入
这些代码。
Drop
trait会自动清理代码
所有权系统确drop
只会在值不再使用时被调用一次
todo:获取&mut self,那么之前不能获取可变引用了?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct CustomSmartPointer { data: String , } impl Drop for CustomSmartPointer{ fn drop (&mut self ){ println! ("Dropping CustomSmartPointer with data `{}`!" , self .data); } } fn main (){ let c = CustomSmartPointer{ data: String ::from("pointer1" )}; let d = CustomSmartPointer{ data: String ::from("pointer2" )}; println! ("CustomSmartPointer created!" ); }
输出顺序如下,变量以被创建时相反的顺序丢弃
1 2 3 CustomSmartPointer created! Dropping CustomSmartPointer with data `pointer1`! Dropping CustomSmartPointer with data `pointer2`!
Rust不允许显示调用drop
函数,因为Rust仍然会在值离开作用域时
调用drop
函数导致double free 的错误。如果需要提早清理,
可以使用std::mem::drop
函数(已经位于prelude中)。
1 2 3 4 5 6 fn man (){ let c = CustomSmartPointer{ data: String ::from("some data" )}; println! ("CumstomSmartPointer Created" ); drop (c); println! ("CustomSmartPointer dropped before the end of main" );
Box<T>
在堆上存储数据,而栈上存放指向堆数据的指针,常用于
类型编译时大小未知,而想要在确切大小的上下文中使用
大量数据希望在拷贝时不转移所有权
只关心数据是否实现某个trait而不是其具体的类型
(trait对像)
1 2 3 4 let b = Box ::new(5 );println! ("b = {}" , b);
创建递归类型 Rust需要在编译时知道类型占用的空间,而递归类型(recursive
type)中值的一部分可以时相同类型的另一个值,所以Rust无法知道
递归类型占用的空间。而box大小已知,可以在递归类型中插入box
创建递归类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 enum List { Cons(i32 , Box <list>), Nil, } use List::{Cons, Nil};fn main (){ let list = Cons(1 , Box ::new(Cons(2 , Box ::new(Cons(3 , Box ::new(Nil)))))); }
Rc<T>
Rc:引用计数reference counting,记录一个值引用的数量判断
这个值是否仍然被使用,如果值只有0个引用,表示没有任何有效
引用,可以被清理。
Rc<T>
允许多个不可变引用,让值有多个所有者 共享数据,
引用计数确保任何所有者存在时值有效。用于在堆上分配内存供
程序多个部分读取,且在无法在编译时确定哪部分最后结束使用
(否则令其为所以者即可)
Rc<T>
只适合单线程场景
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 32 enum List { Cons(i32 , Rc<List>), Nil, } use List::{Cons, Nil};use std::rc::Rc;fn main (){ let a = Rc::new(Cons(5 , Rc::new(Cons(10 , Rc::new(Nil))))); println! ("count after creating a = {}" , Rc::strong_count(&a)); let b = Cons::(3 , Rc::clone(&a)); println! ("count after creating b = {}" , Rc::strong_count(&a)); { let c = Cons::(4 , Rc::clone(&a)); println! ("count after creating c = {}" , Rc::strong_count(&a)); } println! ("count after c goes out of scope = {}" , Rc::strong_count(&a)); }
RefCell<T>
RefCell<T>
是一个遵守内部可变性 模式的类型,允许通过
不可变引用 更改T
值。实际上仍然是通过可变引用更改值,
只是获得的T
的可变引用在RefCell<T>
内部。
RefCell<T>
同样只能应用于单线程场景
可以理解为,将Rust静态引用 改成时分复用 引用,Rust在
运行时进时引用检查,只要保证在运行时任意时刻满足引用规则
即可。
内部可变性interior mutability Rust中的一个设计模式,允许在有不可变引用时改变数据,这违反
了引用规则,因此在该模式中使用unsafe
代码模糊Rust通常的
可变性和引用规则。但是引用规则依然适用,只是在运行时检查,
会带来一定运行时损失。
在确保代码运行时遵守借用规则,即使编译器不能保证,可以选择
使用运用内部可变性模式的类型,涉及的unsafe
代码被封装进
安全的API中,外部类型依然不可变
Ref
、RefMut
Ref = RefCell<T>.borrow()
:获取T
的不可变引用
RefMut = RefCell<T>.borrow_mut()
:获取T
的一个可变引用
Ref
和RefMut
均是实现Deref
trait的智能指针,RefCell<T>
记录当前活动的Ref
和RefMut
指针,调用borrow
时,不可变
引用计数加1,Ref
离开作用域时不可变引用计数减1
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 pub trait Messenger { fn send (&self , msg: &str ); } pub struct LimitTracker <'a , T:'a + Messenger>{ Messenger:&'a T, value: usize , max : usize , } impl <'a , T> LimitTracker<a', T> where T: Messenger{ pub fn new (messenger: &T, max: usize ) -> LimitTracker<T>{ LimitTracker{ messenger, value: 0 , max, } } pub fn set_value (&mut self , value:usize ){ self .value = value; let percentage_of_max = self .max as f64 / self .max as f64 ; if percentage_of_max >= 0.75 { self .messenger.send("Warning: over 75% of quota has been used!" ); } } } #[cfg(test)] mod tests{ use supper::*; use std::cell:RefCell; struct MockMessenger { sent_messages: RefCell<Vec <String >>, } impl MockMessenger{ fn new () -> MockMessenger{ MockMessenger{ sent_messages: RefCell<vec! []> } } } impl Messenger for MockMessenger{ fn send (&self , message: &str ){ self .sent_messages.borrow_mut().push(String ::from(message)); } } #[test] fn it_send_an_over_75_percent_warning_message (){ let mock_messenger = MockMessenger::new(); let mut limit_tracker = LimitTracker::new(&mock_messenger, 100 ); limit_tracker.set_value(80 ); assert_eq! (mock_messenger.sent_messages.borrow().len(), 1 ); } }
Rc<RefCell<T>>
T
值可以修改,且可以被多个所有者拥有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #[derive(Debug)] enum List { Cons(Rc<RefCell<i32 >>, Rc<List>), Nil } use List::{Cons, Nil};use std::rc::Rc;use std::cell::RefCell;fn main (){ let value = Rc::new(RefCell::new(5 )); let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil))); let b = Cons(Rc::new(RefCell::new(6 )), Rc::clone(&a)); let c = Cons(Rc::new(RefCell::new(10 )), Rc::clone(&a)); *value.borrow_value += 10 ; println! ("a after = {:?}" , a); println! ("b after = {:?}" , b); println! ("c after = {:?}" , c); }
RefCell<Rc<T>>
T
值不能改变,但是Rc<T>
整体可以改变,此时可能出现引用循环
,导致内存泄露。引用循环是程序逻辑上的bug,Rust无法捕获。
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 32 33 34 35 36 37 38 39 40 use List::{Cons, Nil};use std::rc::Rc;use std::cell::RefCell;enum List { Cons(i32 , RefCell<Rc<list>>), Nil, } impl List{ fn tail (&self ) -> Option <&RefCell<R<List>>>{ match *self { Cons(_, ref item) => Some (item), Nil => None , } } } fn main (){ let a = Rc::new(Cons(5 , RefCell::new(Rc::New(Nil)))); println! ("a initial rc count ={}" , Rc::strong_count(&a)); println! ("a next item = {:?}" , a.tail()); let b = Rc::new(Cons(10 , RefCell::new(Rc::clone(&a)))); println! ("a rc count after b creating = {}" , Rc::strong_count(&a)); println! ("b initial rc count = {}" , Rc::strong_count(&b)); println! ("b next item = {:?}" <, b.tail()); if let Some (link) = a.tail(){ *link.borrow_mut() = Rc::clone(&b); } println! ("b rc count after changing a = {}" , Rc::strong_count(&b)); println! ("a rc count after changing a = {}" , Rc::strong_count(&a)); }
Weak<T>
强引用Rc<T>
代表共享Rc
实例的引用,代表所有权关系,
而弱引用Weak<T>
不代表所有权关系,不同于Rc<T>
使用
strong_count
计数,Weak<T>
使用weak_count
计数,即使
weak_count
无需为0,Rc
实例也会被清理(只要strong_count
为0)
Weak<T>
指向的值可能已丢弃,不能像Rc<T>
一样直接解引用
,需要调用upgrade
方法返回Option<Rc<T>>
Weak<T>
避免Rc<T>
可能导致的引用循环
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 use std::rc::{Rc, Weak};use std::cell::RefCell;#[derive(Debug)] struct Node { value: i32 , parent: RefCell<Weak<Node>>, Children: RefCell<Vec <Rc<Node>>>, } fn main (){ let leaf = Rc::new(Node{ value: 3 , parent: RefCell::new(Weak::new()), children: RefCell::new(vec! []), }); println! ( "leaf strong = {}, weak = {}" , Rc::strong_count(&leaf), Rc::weak_count(&leaf), ); { let branch = Rc::new(Node{ value: 5 , parent: RefCell::new(Weak::new()), children: RefCell::new(vec! [Rc::clone(&leaf)), }); *leaf.parent.borrow_mut() = Rc::downgrade(&branch); println! ( "branch strong = {}, weak = {}" , Rc::strong_count(&branch), Rc::weak_count(&branch), ); println! ( "leaf strong = {}, weak = {}" , Rc::strong_count(&leaf), Rc::weak_count(&leaf), ); } println! ("leaf parent = {:?}" , leaf.parent.borrow().upgrade()); println! ( "leaf strong = {}, weak = {}" , Rc::strong_count(&leaf), Rc::weak_count(&leaf), ); }
比较
智能指针
数据拥有者
引用检查
多线程
Box<T>
单一所有者
编译时执行可变(不可变)引用检查
是
Rc<T>
多个所有者
编译时执行不可变引用检查
否
RefCell<T>
单一所有者
运行时执行不可变(可变)引用检查
否
Weak<T>
不拥有数据
编译时执行可变(不可变)检查
否
常用枚举类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Option <T>{ Some <T>, None , } some = Some (9 ); some.take(); Result <T, U>{ Ok <T>, Err <U>, }