集合
- 集合中存储的值位于堆之上,这也是说他们不需要在编译的时候知道数据数量。
- 栈和堆的解释在之前有过解释链接: link
vector存储一系列的值
新建vector
let v: Vec<i32> = Vec::new();
- 上段代码创建了一个空的vector,存储类型是i32类型,
- 而在实际过程中,由于
vec!
宏的存在,它会根据数据的类型为我们提供一个vec。
let v = vec![1,2,3];
更新vec
let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);
v.push(8);
- 使用push方法在其中增加新的变量,同时注意使用
mut
变量进行变化。
丢弃vector会同时丢弃所有的元素
{
let v= vec![1,2,3,4];
}
- vector变量在这段代码中的作用域即为程序块中,在离开程序块之后,vector中的元素会被抛弃。如果使用vec中的元素的引用,就会出现下面的状况:
读取vector的元素
- 读取vec中的元素有两种方法,一种是索引语法,另外一种是
get
方法
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 emelnment is {}",third),
None => println!("There is no third element."),
}
- 使用
&
和[]
返回一个引用;或者使用get
方法以索引作为参数来返回一个Option<&T>
。由于前面代码使用过third
来获取&v[2]
中的内容,所以之后的结果中显而易见,使用option<&T>
来获取third
的引用。 - 关于
Option<&T>
的有关内容,链接: link
使用索引语法或者get获取vector中的项。
let v = vec![1,2,3,4,5];
let does_not_exist = &v[100];
let does_not_exist = v.get(100);
官方解释:
-
当运行这段代码,你会发现对于第一个 [] 方法,当引用一个不存在的元素时 Rust 会造成 panic。这个方法更适合当程序认为尝试访问超过 vector 结尾的元素是一个严重错误的情况,这时应该使程序崩溃。
-
当 get 方法被传递了一个数组外的索引时,它不会 panic 而是返回 None。当偶尔出现超过 vector 范围的访问属于正常情况的时候可以考虑使用它。接着你的代码可以有处理 Some(&element) 或 None 的逻辑,如第 6 章讨论的那样。例如,索引可能来源于用户输入的数字。如果它们不慎输入了一个过大的数字那么程序就会得到 None 值,你可以告诉用户当前 vector 元素的数量并再请求它们输入一个有效的值。这就比因为输入错误而使程序崩溃要友好的多!
vector中相同作用域中不可以同时存在可变和不可变引用
- 使用官方文案进行解释
vector中的内存位于堆之上,而堆中的内存是提前分配好大小的,这样的结果会造成可能添加新元素的结果中会添加新的内存空间,而之前的内存空间的地址引用会因此而改变,这样的结果是不可估计的,Rust会自动阻止这种情况的发生。
vector中元素的遍历
let v =vec![100,32,57];
for i in &v
{
println!("{}",i);
}
- 这种是直接遍历其中的元素,下面则是对可变vector中的元素每个均添加50
let mut v = vec![100,32,57];
for i in &mut v
{
*i +=50;
}
使用枚举存储多种类型
- vector只能存储同一种类型的元素,而之前介绍过的有元组、枚举均可添加不同类型的值。
enum SpreadsheetCell{
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Float(10.12),
SpreadsheetCell::Text(String::from("blue")),
];
字符串(String)的使用
新建字符串
let mut s = String::new();
- 新建空的字符穿串
to_string
方法,能够用于任何实现了Display
trait类型,字符串字面量也实现了它,
let data = "initial contents";
let s= data.to_string();
let s= "initial contents".to_string();
- 这两种方法均实现了string字符串,第一种方法是使用了先创建字符串字面量,第二种方法是直接使用字符串字面量进行。
- 同时也可以通过使用
String::from
从字符串字面量创建String
,等同于to_string
.
let s = String::from("initial contents");
同时,rust使用的unicode中包括了多种数据,类似特殊字符和emoji也可以。
fn main() {
let hello = String::from("السلام عليكم");
let hello = String::from("Dobrý den");
let hello = String::from("Hello");
let hello = String::from("שָׁלוֹם");
let hello = String::from("नमस्ते");
let hello = String::from("こんにちは");
let hello = String::from("안녕하세요");
let hello = String::from("你好");
let hello = String::from("Olá");
let hello = String::from("Здравствуйте");
let hello = String::from("Hola");
let hello = String::from("😻");
}
更新字符串
- String中的内容可以改变,大小也可以改变,类似于在
vec
之中放入更多的数据,同时可以方便使用+
或者format!
来拼接String
值。
使用push_str和push附加字符串
- 通过使用
push_str
方法添加字符串slice,可以增加string的长度。
let mut s = String::form("foo");
s.push_str("bar");
- 结果是foobar
- 使用push_str获取的时候,使用字符串slice的话,会夺走它的所有权。
fn main() {
let mut s1 = String::from("foo");
let s2 = "bar";
s1.push_str(s2);
println!("s2 is {}", s2);
}
- 这段代码可以正常运行,虽然
s2
的所有权被函数获取了。
- 使用push方法将字符加入到String之中,它会获取一个单独的字符为参数,附加到String之中。
fn main() {
let mut s = String::from("lo");
s.push('l');
}
使用+运算符或者format!宏拼接字符串
let s1 = String::from("hello,");
let s2 = String::from("world!");
let s3 = s1+&s2;
- 在使用完成之后,
s3
将包括s1
和s2
之中的内容,但是s1
是使用的函数本身,而&s2
是使用s2
的引用,因此,s2
之后仍具有所有权,而s1
不行,这种情况是由add函数所定义的。 - 具体可以看下面的官方文档
- 而对于多个字符串的连接,则需要使用
format!
宏定义,如果使用add函数
会显得较为复杂。
//使用add函数
fn main() {
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = s1 + "-" + &s2 + "-" + &s3;
}
//使用format!宏
fn main() {
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);
}
字符串的索引
Rust中的字符串不支持索引,因为
- 每一个unicode在不同语言之下,它的字节数不同
- 字符串中包括字节、标量值和字形簇
- 字符串中的时间复杂度未知,因为要从开始直到结束处遍历字符总数
下面是官方描述
遍历字符串的方法
- Rust对字符串元素的获取方法有另外的方法。
- 使用
chars
方法来对字符串进行遍历。
for c in "aaaasssss".chars(){ println!("{}",c);}
- 使用
- bytes方法返回每个原始字节。
- 使用
bytes
方法返回原始字节
- 使用
for b in "aaaacccc".bytes()
{
println!("{}",b)
}
- 这段代码会打印出对应的
String
字节,对应的值为unicode
格式。此处应该熟记a=97,A=65,大小写转换差32。
哈希(hash) map
- HashMap<K,V>类型储存了一个键类型
k
对应一个值类型V
的映射,通过哈希函数实现映射,决定如何将键和值放入内存中。 - 哈希map使用键作为寻找数据的方法,与vector不同。
- 哈希表没有被
prelude
引用,需要自己添加头文件 - 哈希表将数据存储在堆之上。
新建哈希表
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"),10);
scores.insert(String::from("Yellow"),50);
- 使用
new
创建新的哈希表,并且使用insert
插入数据。 - 创建之后
Blue
有10分,Yellow
有50分 - 另外一种方法是使用
collect
方法创建 - collect方法是使用zip方法创建元组vector,然后再使用collect方法将vector转换为HashMap。
fn main() {
use std::collections::HashMap;
let teams = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
}
HashMap<_ , _>
类型标注和其他的不同,在其他的数据中使用collect显示转换,而HashMap则不同,只需使用下划线_进行标注,同时根据后面的vector来推断前面的类型。
哈希map和所有权
i32是一种Copy trait类型,关于Copy trait的解释,链接: link
- 对于像String之类的数据,他的所有权会被移动给HashMap,而
Copy trait
不会
use std::collections::HashMap;
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");
let mut map = HashMap::new();
map.insert(field_name,field_value);
- 在这段代码之后
field_name
和field_value
就不能够再使用。不想要这种结果,可以使用引用来传递值,但是这些值的生命周期必须至少包括HashMap
的生命周期
HashMap中值的访问
- 使用
get
方法并提供对应键从哈希map中获取值
fn main() {
use std::collections::HashMap;
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);
}
- get返回的
Option<V>
类型,链接: link - 所以此时返回的值为
Option<i32>
,对应着的是some(10),而对应着的,如果返回值为None,则需要使用match
匹配。 - 如果想要遍历哈希map中的每一个键值对,使用for循环:
fn main() {
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
for (key, value) in &scores {
println!("{}: {}", key, value);
}
}
更新哈希map
- 哈希Hash中可以选择完全无视旧值并用新值代替旧值。可以选择保留旧值而忽略新值,并只在键 没有 对应值时增加新值。或者可以结合新旧两值。
覆盖一个值
- 哈希表之中不能够使用
#[derive(Debug)]
,这种形式仅仅在enum,struct、union中使用,若是想打印,直接打印即可
fn main() {
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25);
println!("{:?}", scores);
}
只在键没有对应值时插入
fn main() {
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Blue")).or_insert(50);
println!("{:?}", scores);
}
entry
的返回值是一个枚举,如果有的话,就不执行or_insert
,如果没有,执行or_insert
根据旧值更新一个值
use std::collection::HashMap;
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whiltespace()
{
let count = map.entry(word).or_insert(0);
*count += 1;
}
println!("{:?}",map);