1、Vector
使用Vector存储多个值
vec<T>,叫做vector
由标准库提供
可存储多个值
只能存储相同类型的数据值
在内存中连续存放
创建Vector
Vec:new函数
fn main() {
let v: Vec<i32> = Vec::new();
}
使用初始值创建Vec<T>,使用vec!宏
fn main() {
let v = vec![1,2,3,4,5];
}
更新Vector
向Vector添加元素,使用push方法
fn main() {
let mut v = Vec::new();
v.push(1);
}
删除Vector
与任何其它struct一样,当Vector离开作用域后
它就被清理掉了
读取Vector的元素
两种方式可以引用Vector里的值;
fn main() {
let v = vec![1,2,3,4,5];
let third:&i32 = &v[2];
println!("The third element is {}",third);
match v.get(2) {
Some(third) => println!("The third element is {}",third),
None => println!("There is no third element"),
}
}
索引vs get处理访问越界
所有权和借用规则
不能在同一作用域内同时拥有可变和不可变引用
fn main() {
let mut v = vec![1,2,3,4,5];
let first:&i32 = &v[0];
v.push(6);
println!("The third element is {}",first);
}
这里就会报错,因为它已经进行了一个不可变的引用,现在又进行可变的引用,所以就会报错。
遍历Vector 中的值
for循环
fn main() {
let v = vec![1,2,3,4,5];
for i in &v {
println!("{}",i)
}
}
修改里面的值:
fn main() {
let mut v = vec![1,2,3,4,5];
for i in &mut v {
*i += 50;
}
for i in &v {
println!("{}",i)
}
}
2、Vector+enum
使用enum来存储多钟数据类型
Enum的变体可以附加不同类型的数据
Enum的变体定义在同一个enum类型下
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String)
}
fn main() {
let row = Vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
}
3、String
创建和更新
Rust开发者经常会被字符串困扰的原因
Rust倾向于暴露可能的错误
字符串数据结构复杂
UTF-8
字符串是什么?
Byte 的集合
一些方法能将byte解析为文本
Rust的核心语言层面,只有一个字符串类型:字符串切片str(或&str)
字符串切片:对存储在其它地方、UTF-8编码的字符串的引用
字符串字面值:存储在二进制文件中,也是字符串切片
String类型
来自标准库而不是核心语言
可增长、可修改、可拥有
UTF-8编码
通常说的字符串是指?
String和 &str
标准库里用的多
UTF-8编码
其它类型的字符串
Rust的标准库还包含了很多其它的字符串类型,例如:OsString、OsStr、CStrng、CStr
String vs Str后缀:拥有或借用的变体
可存储不同编码的文本或在内存中以不同的形式展现
Library crate针对存储字符串可提供更多的选项
创建一个新的字符串(String)
很多Vec<T>的操作都可用于String。
String::new()函数
fn main() {
let mut s = String::new();
}
使用初始值来创建String:
to_string()方法,可用于实现了Display trait的类型,包括字符串字面值
fn main() {
// 字符串字面值(字符串切片)
let data = "init";
// 转化为字符串
let s = data.to_string();
// 直接转化为字符串
let s1 = "init".to_string();
}
String::from()函数,从字面值创建String
fn main() {
let s = String::from("init");
}
更新String
push_str()方法:把一个字符串切片附加到String
fn main() {
let mut s = String::from("foo");
s.push_str("bar");
println!("{}",s)
}
push()方法:把单个字符附加到String
fn main() {
let mut s = String::from("lo");
s.push('l');
println!("{}",s)
}
+:连接字符串
fn main() {
let s1 = String::from("Hello");
let s2 = String::from(" World!");
let s3 = s1 + &s2;
println!("{}",s3);
println!("{}",s1);
println!("{}",s2);
}
s1是String类型的,但是s2是字符串的切片或者是字符串的引用
s2在拼接时候还可以继续使用,但是s1在拼接之后就不能够使用了
这是因为:
+使用了类似这个签名的方法 fn add(self, s: &str) -> String { ... }
标准库中的add方法使用了泛型
只能把&str添加到String
解引用强制转换( deref coercion)
format!:连接多个字符串
fn main() {
let s1 = String::from("tic");
let s2 = String::from(" tac!");
let s3 = String::from(" toe!");
// let s3 = s1 + "_" + &s2 + "_" + &s3;
// println!("{}",s3);
let s3 = format!("{}-{}-{}",s1,s2,s3);
println!("{}",s3);
println!("{}",s1);
println!("{}",s2);
}
上面我们看到 s1,s2也同样能够输出 表示format!不会获得他们的所有权
对String按索引的形式进行访问
按索引语法访问String的某部分,会报错
fn main() {
let s1 = String::from("hello");
let h = s1[0];
}
Rust的字符串不支持索引语法访问
内部表示
String是对Vec<u8>的包装
len方法
fn main() {
let s1 = String::from("我").len();
// UniCode 标量值
println!("{}",s1);
}
字节、标量值、字形簇
Bytes, Scalar Values, Grapheme Clusters
Rust有三种看待字符串的方式:
字节
fn main() {
let s1 = String::from("我");
for b in s1.bytes() {
println!("{}",b)
}
}
标量值
fn main() {
let s1 = String::from("我");
for b in s1.chars() {
println!("{}",b)
}
}
字形簇(最接近所谓的“字母”) 这个官方的标准库没有支持,可以自行去第三方库实现
Rust不允许对String进行索引的最后一个原因:
索引操作应消耗一个常量时间(O(1))
而String无法保证:需要遍历所有内容,来确定有多少个合法的字符。
切割String
可以使用和一个范围来创建字符串的切片
fn main() {
let s1 = String::from("我");
let s1 = &s1[0..3];
println!("{}",s1)
}
这里中文是占用三个,所以上面我们能输出”我“这个文中,但是如果把它修改为0..2就会painc
必须谨慎使用
如果切割时跨越了字符边界,程序就会 panic。
(b1,b2).(b3,b3),(b4,b5),(b7,b7) 如果这样切割就会发生painc
遍历String的方法
fn main() {
let s1 = String::from("我");
for b in s1.chars() {
println!("{}",b)
}
}
String不简单
Rust选择将正确处理String 数据作为所有Rust程序的默认行为一程序员必须在处理UTF-8数据之前投入更多的精力
可防止在开发后期处理涉及非ASCIl字符的错误。
3、HashMap
HashMap<K,V>
键值对的形式存储数据,一个键(Key)对应一个值(Value)
Hash函数:决定如何在内存中存放K和V
适用场景:通过K(任何类型)来寻找数据,而不是通过索引
创建HashMap
创建空HashMap: new()函数
添加数据: insert()方法
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(String::from("key"), 56);
}
HashMap 用的较少,不在 Prelude 中
标准库对其支持较少,没有内置的宏来创建HashMap
数据存储在heap 上
同构的。一个HashMap 中:
所有的K必须是同一种类型
所有的V必须是同一种类型
另一种创建HashMap 的方式: collect方法
在元素类型为Tuple 的Vector上使用collect方法,可以组建一个HashMap:
要求Tuple有两个值:一个作为K,一个作为V
collect方法可以把数据整合成很多种集合类型,包括HashMap
返回值需要显式指明类型
use std::collections::HashMap;
fn main() {
let teams = vec![String::from("Blue"),String::from("Yellow")];
let intial_scores = vec![10,50];
let scores :HashMap<_,_> =
teams.iter().zip(intial_scores.iter()).collect();
}
HashMap和所有权
对于实现了Copy trait的类型(例如 i32),值会被复制到HashMap 中。
对于拥有所有权的值(例如String),值会被移动,所有权会转移给HashMap-(例子)
如果将值的引用插入到HashMap,值本身不会移动
use std::collections::HashMap;
fn main() {
let field_name = String::from("Favoirte color");
let field_value = String::from("Blue");
let mut map = HashMap::new();
// map.insert(field_name, field_value);
// println!("{},{}",field_name,field_value);
map.insert(&field_name, &field_value);
println!("{},{}",field_name,field_value)
}
在HashMap有效的期间,被引用的值必须保持有效
访问HashMap中的值
get 方法
―参数:K
一返回:Option<& V>
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(String::from("Blue"), 10);
map.insert(String::from("Yellow"), 50);
let tem_name = String::from("Blue");
let score = map.get(&tem_name);
match score {
Some(s)=> println!("{}",s),
None => println!("tem not exist")
}
}
遍历HashMap
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(String::from("Blue"), 10);
map.insert(String::from("Yellow"), 50);
for (k,v) in map{
println!("{},{}",k,v)
}
}
更新HashMap<K,V>
HashMap大小可变
每个K同时只能对应一个V
更新HashMap 中的数据:
K已经存在,对应一个V
替换现有的V
保留现有的V,忽略新的V。
合并现有的V和新的V
K不存在
添加一对K,V
替换现有的V
如果向 HashMap插入一对 KV,然后再插入同样的K,但是不同的V,那么原来的V会被替换掉
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(String::from("Blue"), 10);
map.insert(String::from("Blue"), 50);
println!("{:?}",map);
}
只在K不对应任何值的情况下,才插入V
entry方法:检查指定的K是否对应一个V
参数为K
返回enum Entry:代表值是否存在
Entry 的 or_insert()方法:―返回:
如果K存在,返回到对应的V的一个可变引用
如果K不存在,将方法参数作为K的新值插进去,返回到这个值的可变引用
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(String::from("Blue"), 10);
// map.entry(String::from("Yellow")).or_insert(50);
let e = map.entry(String::from("Yellow"));
println!("{:?}",e);
e.or_insert(50);
map.entry(String::from("Blue")).or_insert(50);
println!("{:?}",map);
}
基于现有V来更新V
use std::collections::HashMap;
fn main() {
let text = "hello world winderful world";
let mut map = HashMap::new();
for word in text.split_ascii_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}
println!("{:?}",map);
}
Hash函数
默认情况下,HashMap使用加密功能强大的Hash 函数,可以抵抗拒绝服务(DoS)攻击。
不是可用的最快的Hash
算法但具有更好安全性。
可以指定不同的hasher来切换到另一个函数。
hasher是实现BuildHasher trait的类型