请选择 进入手机版 | 继续访问电脑版
搜索
房产
装修
汽车
婚嫁
健康
理财
旅游
美食
跳蚤
二手房
租房
招聘
二手车
教育
茶座
我要买房
买东西
装修家居
交友
职场
生活
网购
亲子
情感
龙城车友
找美食
谈婚论嫁
美女
兴趣
八卦
宠物
手机

Rust入坑指南:鳞次栉比

[复制链接]
查看: 105|回复: 0

1万

主题

2万

帖子

5万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
52033
发表于 2019-12-3 03:44 | 显示全部楼层 |阅读模式
很久没有挖Rust的坑啦,本日来挖一些排列整洁的坑。没错,就是要先容一些聚集典范的数据典范。“鳞次栉比”这个题目能否是显得很有文化?
Rust入坑指南:常规套路一文中我们已经先容了一些底子数据典范了,它们都存储在栈中,本日我们重点先容3种数据典范:string,vector和hash map。
String

String典范我们在之前的进修中已经有了较多的兵戈,可是没有举行过具体的先容。有些有编程根柢的同学大要不屑于进修String典范,究竟它在全数编程说话中可以说是最常用的典范了,大家也都很熟悉了。对于有这类心理的同学,我想对他们说:我刚起头也是这样想的,直到后来我被编译器揍的满头包,才下定决心归来认真进修一下String典范。
Rust的字符串分为以下几种典范:

  • str:表示牢固长度的字符串
  • String:表示可增加的字符串
  • CStr:表示由C分派,被Rust借用的字符串,一样平常用于和C说话交互
  • CString:表示由Rust分派而且可以转达给C说话的字符串
  • OsStr:表示和操纵系统关连的字符串,严重为了兼容Windows
  • OsString:OsStr的可变版本
  • Path:表示途径
  • PathBuf:是Path的可变版本
本文我们重点会商前两种,由于它们是斥地进程中最常用的,也是比力轻易肴杂的。对于str,我们常见的是它的援用典范,&str。假如你看过了Rust入坑指南:焦点概念一文后,相信你已经了解了援用典范和Ownership的概念。也就是说String典范具有Ownership而&str没有。
在Rust中,String本质上是Vec,Vec是向量聚集的关键字,我们在背面会先容。String典范由三个部分组成,别离是:指向堆中字节序列的指针,记录堆中字节序列的长度和堆分派的容量。经过一段代码大要你很有更深的大白。
  1. fn main() {    let mut a = String::from("foo");    println!("{:p}", a.as_ptr());    println!("{:p}", &a);    assert_eq!(a.len(), 3);    a.reserve(10);    assert_eq!(a.capacity(), 13);}
复制代码
在这段代码中我们可以看到,a.as_ptr()获得指针和&a获得的指针是纷歧样的。
Rust入坑指南:鳞次栉比  游戏 rust06-1

这里我们表白一下,as_ptr获得到的指针是堆中字节序列的指针地址,而&a的地址是字符串变量在栈上的指针地址。此外,len()和capacity()方式获得的长度都是字节数目,而非字符数目。这里你可以自己脱手试试中翰墨符的长度。
聊完了字符串的底子概念今后,相信你已经对Rust的字符串有了一个大要的熟悉。接下来我们就一路来看一看字符串的CRUD的方式吧。
建立字符串

话不多说,先来展现一下建立字符串的各类方式。
  1. fn main() {    let string: String = String::new();    let string: String = String::from("hello rust");    let string: String = String::with_capacity(10);    let str: &'static str = "Jackey";    let string: String = str.to_owned();    let string: String = str.to_string();}
复制代码
我们比力常用的是前两种,下面先容一下背面几个方式。with_capacity()是建立一个空字符串,参数表示在堆平分派的字节数。to_owned和to_string是演示了怎样把&str典范转换成String典范。
点窜字符串

Rust点窜字符串的常用方式也有很多,例如在字符串后追加,毗连两个字符串,更新字符串等。下面这段代码就展现了一些点窜字符串的方式。
  1. fn main() {    let mut hello = String::from("Hello, ");    hello.push('J');    // 追加单个字符    hello.push_str("ackey! ");    //追加字符串    println!("push: {}", hello);    hello.extend(['M', 'y', ' '].iter());   //追加多个字符,参数为迭代器    hello.extend("name".chars());    println!("extend: {}", hello);    hello.insert(0, 'h');   //类似于push,可以指定插入的位置    hello.insert(1, 'a');    hello.insert(2, '!');    hello.insert_str(0, "Haha");    println!("insert: {}", hello);    let left = "Hello, ".to_string();    let right = "World".to_string();    let result = left + &right;    println!("+: {}", result);   //利用+毗连字符串时,第二个必须为援用    let mut message = "rust".to_string();   //利用+=毗连字符串时,字符串必须界说为可变    message += "!";    println!("+=: {}", message);    let s = String::from("foobar");    let s: String = s        .chars()        .enumerate()        .map(|(_i, c)| {c.to_uppercase().to_string()})        .collect();    println!("update chars: {}", s);      let s1 = String::from("hello");    let s2 = String::from("rust");    let s3 = format!("{}-{}", s1, s2);    println!("format: {}", s3);}
复制代码
我们对上面的代码做一些补充的表白。
push和insert类似,带有_str的方式吸收的参数是字符串,否则只能吸收单个字符。insert可以指定插入的位置,而push只能在字符串末端插入。
利用「+」毗连字符串时,第一个参数是String典范,第二个则必如果援用典范&str。这类似于我们挪用一个add方式,它的界说是这样的:
  1. fn add(self, s: &str) -> String {
复制代码
所以,第一个参数的ownership转移到了函数中,又经过返回结果转达出来。也就是说,在利用了+操纵符以后,left已经没有ownership了。
字符串查找

在Rust中,字符串是不能按照位置来获得到指定字符的。也就是下面这段代码是编译不外的。
  1. let s1 = String::from("hello");let h = s1[0];
复制代码
由于,Rust会以为这个0是指第一个字节,而Rust字符串中的字符大要占据多个字节(还记得前面我让你用中翰墨符尝试代码吗?)所以,假如你纯真的想要获得一个字节,编译器不晓得你是真的想要获得字节对应的数值,还是要获得那个字符。
我们在处置赏罚字符串时凡是有以下方式:
  1. fn main() {    let hello = "Здравствуйте";    let s = &hello[0..4];    println!("{}", s);    let chars = hello.chars();    for c in chars {        println!("{}", c);    }    let bytes = hello.bytes();    for byte in bytes {        println!("{}", byte);    }    let get = hello.get(0..1);    let mut s = String::from("hello");    let get_mut = s.get_mut(3..5);    let message = String::from("hello-world");    let (left, right) = message.split_at(6);    println!("left: {}, right: {}", left, right);}
复制代码
凡是是利用字符切片,也可以利用chars方式获得到Chars迭代器,然后可以对每个字符举行零丁处置赏罚。此外,利用get或get_mut方式也可以吸收索引范围,返回指定的字符串切片。返回结果是Option典范,这是由于假如指定的索引返回不能返回完整字符,那末Rust就会返回None。这里也可以利用is_char_boundary方式来判定一个位置能否好坏法界限。
末端,也可以利用split_at或split_at_mut方式来朋分字符串。这要求朋分的位置恰好是字符界限位置,假如不是,步伐就会崩溃。
删除字符串

Rust的标准库供给了一些删除字符串的方式,我们来演示一些:
  1. fn main() {    let mut hello = String::from("hello");    hello.remove(3);    println!("remove: {}", hello);    hello.pop();    println!("pop: {}", hello);    hello.truncate(1);    println!("truncate: {}", hello);    hello.clear();    println!("clear: {}", hello);}
复制代码
结果如图:
Rust入坑指南:鳞次栉比  游戏 rust06-2

remove方式用来删除字符串中的某个字符,其吸收的参数是字符的肇端位置,假如能否是某个字符的肇端位置,会致使步伐崩溃。
pop方式会弹出字符串末端的字符,truncate方式是截取指定长度字符串,而clear方式例是用来清空字符串。
至此,关于Rust中的字符串的底子概念和CRUD我们都已经先容完了,接下来我们再来看另一种聚集典范Vector。
Vector

Vector是用来存储类似数据典范的多个数据一种数据典范。它的关键字是Vec。下面我们一路来看看向量的CRUD吧。
建立向量
  1. fn main() {    let v1: Vec = Vec::new();    let v2 = vec![1, 2, 3];}
复制代码
上面这段代码演示了建立一个向量的两种方式,第一种是利用new函数来建立一个空的向量,由于没有增加元素,所以要显式的指定存储元素的典范。第二种是建立一个有初始值的向量聚集,我们间接利用vec!宏,然后指定初始值即可,不必要指定向量中元素的数据典范,由于编译器可以自己揣度出来。
更新向量
  1. fn main() {    let mut v = Vec::new();    v.push(1);    v.push(2);}
复制代码
建立一个空的向量以后,假如我们想要增加元素,便可以间接利用push方式,向末端追加元素。
删除向量
  1. fn main() {    let mut v = Vec::new();    v.push(1);    v.push(2);    v.push(3);    v.pop();    for i in &v {        println!("{}", i);    }}
复制代码
删除单个元素可以利用pop方式,而要删除全部向量,只能像其他结构体一样,到其ownership生效。
读取向量元素
  1. 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."),    }    let v = vec![100, 32, 57];    for i in &v {        println!("{}", i);    }}
复制代码
当你必要读取单个指定元素时,有两种方式可以用,一种是利用[],另一种是利用get方式。两种方式的区分是:第一种返回的是元素的典范,而get返回的是Option典范。假如你指定的位置越界了,那末利用第一种方式步伐会间接崩溃,而利用第二种方式例会返回None。
此外,还可以经过遍历向量的形式来读取元素。假如想要存储不同典范的数据,我们可以借助罗列典范。
  1. fn main() {    enum SpreadsheetCell {        Int(i32),        Float(f64),        Text(String),    }    let row = vec![        SpreadsheetCell::Int(3),        SpreadsheetCell::Text(String::from("blue")),        SpreadsheetCell::Float(10.12),    ];}
复制代码
HashMap

HashMap存储了KV结构的数据,各个Key必须是同一种典范,各个Value必须是同一种典范。由于HashMap是三种聚集典范中利用最少的,所以在利用之前,必要手动引入进来
  1. use std::collections::HashMap;
复制代码
建立HashMap

首先我们来了解一下怎样建立一个新的Hash Map并增加元素。
  1. use std::collections::HashMap;fn main() {    let field_name = String::from("Favorite color");    let field_value = String::from("Blue");    let mut map = HashMap::new();    map.insert(field_name, field_value);}
复制代码
留意,在利用insert方式时,field_name和field_value城市落空全数权。那怎样再利用它们呢?我们只能从Hash Map中再拿出来。
拜候Hash Map的数据
  1. use std::collections::HashMap;fn main() {    let field_name = String::from("Favorite color");    let field_value = String::from("Blue");    let mut map = HashMap::new();    map.insert(field_name, field_value);    let favorite = String::from("Favorite color");    let color = map.get(&favorite);    match color {        Some(x) => println!("{}", x),        None => println!("None"),    }}
复制代码
可以看到,我们利用get可以获得到指定Key的值,get方式返回的是Option典范,假如没有指定的Value,则会返回None。此外,也可以利用for循环来遍历Hash Map。
  1. use std::collections::HashMap;fn main() {    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);    }}
复制代码
更新Hash Map

当我们向同一个Key insert值时,旧的值就会被覆盖。假如只想要在Key不存在时插入,则可以利用entry。
  1. use std::collections::HashMap;fn main() {    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);}
复制代码
总结

本日带大家一路挖了三个坑,string,vector和hash map,别离先容了每种数据典范的CRUD。对string的先容占了比力大的篇幅,由于它是最常用的数据典范之一。固然这部分的关连常识还有很多,接待大家和我一路进修交换。

免责声明:假如加害了您的权益,请联系站长,我们会实时删除侵权内容,感谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2006-2014 妈妈网-中国妈妈第一,是怀孕、育儿、健康等知识交流传播首选平台 版权所有 法律顾问:高律师 客服电话:0791-88289918
技术支持:迪恩网络科技公司  Powered by Discuz! X3.2
快速回复 返回顶部 返回列表