type
status
date
slug
summary
tags
category
icon
password
variables6.rs
关于const:
Rust的常量必须加类型。那为什么常量不推导呢? 这是因为故意这样设计的,指定类型就不会因为自动推导类型出问题。
primitive_types3.rs
数组(array)的三要素:
- 长度固定
- 元素必须有相同的类型
- 依次线性排列
数组必须要初始化,以下这种写法会报错:
改为这样就编译通过:
vecs2.rs
iter_mut():
iter_mut() 创建一个可变引用迭代器。当你想要修改集合中的元素时,应使用 iter_mut()。iter_mut() 返回的迭代器将生成集合中每个元素的可变引用。
move_semantics2.rs
方法1:
将vec0的内容clone一份传进函数,然后返回值的所有权交给vec1,此时vec1=[22, 44, 66],vec0=[]
然后再把vec0的内容clone一份传进函数,然后返回值的所有权交给vec0,此时vec1=[22, 44, 66],vec0=[22, 44, 66]
这个时候不管是vec0还是vec1都拥有一片自己的堆上的空间,二者互不相关,因此vec1.push(88)只会改变vec1的值,且vec0的值也还存在
方法2:
首先,创建了vec0的一个可变引用&mut vec0,将这个可变引用传入函数,函数接受一个可变引用类型,然后对其进行操作,也就是操作了vec0指向的那片堆,因此在函数内部,vec0就已经变成了[22, 44, 66]
然后最后返回vec.to_vec(),相当于又创建了一个新的vec,作为vec1绑定的值,因此vec1和vec0又变成了互不相关的
move_semantics4.rs
这个的意思就是函数不再接收参数,而是直接在里面新创建一个包含[22, 44, 66]的vector返回
structs3.rs
结构体,有几点值得注意:
- 初始化实例时,每个字段都需要进行初始化
- 初始化时的字段顺序不需要和结构体定义时的顺序一致
需要注意的是,必须要将结构体实例声明为可变的,才能修改其中的字段,Rust 不支持将某个结构体某个字段标记为可变。
结构体方法:
- Unlike functions, methods are defined within the context of a struct,and their first parameter is always
self
, which represents the instance of the struct the method is being called on.
- we still need to use the
&
in front of theself
shorthand to indicate that this method borrows theSelf
instance, just as we did inrectangle: &Rectangle
. Methods can take ownership ofself
, borrowself
immutably, as we’ve done here, or borrowself
mutably, just as they can any other parameter.
- 方法参数里面不止&self:
package.get_fees(cents_per_gram)
比如这个,get_fees是结构体的方法,它在结构体里面是这样定义的:
也就是说,第二个参数跟在&self后面就好了,在外部调用结构体时只需要传入那个另外的参数
例子:
enums2.rs
更为复杂的枚举:
Move:包含了一个匿名结构体
Echo:包含了一个String
ChangeColor:包含了三个整数
Quit:没有关联任何数据
enums3.rs
模式匹配和模式绑定
strings3.rs
- 字符串的字面量是切片
一般写字符串可以这样写:
let s = "Hello, world!";
实际上,
s
的类型是 &str
,因此你也可以这样声明:let s: &str = "Hello, world!";
- String转&str:取引用即可
- &str转String:
"hello,world".to_string()
或者String::from("hello,world")
- String的操作(必须把String声明为mut)
push()
,在末尾追加字符;push_str()
,在末尾追加字符串insert()
方法插入单个字符,insert_str()
方法插入字符串字面量replace
该方法可适用于String
和&str
类型
由于
String
是可变字符串,因此只有String
这种字符串可以被操作更改这两个方法都是在原有的字符串上追加,并不会返回新的字符串,即返回值是()
也是在原有的字符串上面操作,没有返回值
该方法是返回一个新的字符串,而不是操作原来的字符串
hashmaps2.rs
问题1:
关于hashmap的更新实例中:为什么修改count的值就可以修改hashmap的键值呢?
解答:
map.entry(word)
返回了一个 Entry
枚举类型的值,该枚举有两个变体:Occupied
和 Vacant
。Entry
枚举表示 HashMap
中某个键对应的条目。当调用
or_insert
方法时,如果 word
对应的条目已经存在,则 or_insert
方法会返回该条目的值的可变引用(Occupied
变体),如果该条目不存在,则会在 map
中插入一个新的键值对,然后返回新插入的值的可变引用(Vacant
变体)问题2:
这道题里面有这样一个语句:
其中,basket是这样来的:
所以,为什么basket已经是一个hashmap了,还要用*解引用呢?
解答:
*解引用解的不是basket,而是basket.get(&Fruit::Apple),因为get会返回一个指向该条目键值的引用
跟这段代码一个道理:
or_insert()会返回一个指向该条目键值的引用,因此也需要把它解引用来跟5比较
quiz2.rs
这道题主要注意Append这个命令:迭代器是向量中每个元素的引用(想想这是肯定的,不然在for循环里面所有权都丢失了的话有点太危险),而这种引用默认就是不可变引用,那么使用String类型的push_str()自然就是要报错的,因为这个方法是改变原字符串而不是返回一个新的字符串。
options2.rs
主要是if let语句和while let语句的使用
首先,Option实际上是一种枚举类型,包含两个枚举成员:
所以下面代码中的if let Some(word) = optional_target {}实际上就是将optional_target这个枚举类型的值结构到word上,然后进行一个操作
while let语句也是一个道理,由于optional_integers的元素都是Some类型的变量,因此首先要把optional_integers的值解构到Some(integer)上,然后再进行操作,因此出现一个Some包裹一个Some
关于if let的详细讲解
options3.rs
ref和&:
errors2.rs
关于errors:
- 关于parse:
parse返回的是一个Result枚举类型,成功的话将会是Result::Ok(),失败是话是Result::Err,因此想获取parse成功时返回的正确值,需要将返回值unwrap()
而做题的时候这个例子,返回的错误类型是
std::num::ParseIntError
还可以用?:
errors3.rs
在函数里面使用?:
因为?的逻辑是这样的:
如果返回的不是Err,就正常运行并且将Result枚举类型的值结构给cost
但如果返回的是Err,函数就会立刻终止,然后返回一个Result::Err类型的值,因此必须给函数设置返回值
但是在main函数里面返回一个
Result::Ok(String::from("implement success!"))
是不合法的,因为main
函数通常有两种有效的返回类型:()
(表示成功)和Result<(), E>
(表示成功或出现错误)。main
函数的返回类型必须实现Termination
trait,该trait规定了程序的终止行为errors4.rs
可以像这样用匹配:
但感觉这里也可以直接if判断:
errors6.rs
转换错误类型:将标准库定义的错误类型转换为自定义的ParsePosNonzeroError
关于map_err:
- Maps a Result<T, E> to Result<T, F> by applying a function to a contained Err value, leaving an Ok value untouched.
- 也就是不触发错误返回,而是等待下一步对这个错误的处理
代码:
- 首先
s.parse()
尝试将s进行转换: - 如果出错的话,
map_err()
就会返回ParsePosNonzeroError::from_parseint
这个自定义的错误类型然后直接返回,而不是ParseIntError
- 如果没出错,
map_err()
就会返回一个Result::Ok(value)作为s转换后的值——因此后面一定要加?,不然出错的情况确实可以正常返回,但是没出错时Result类型的值是无法与i64类型的x绑定的,而?可以将正常返回的值直接unwrap
- 然后x被成功绑定一个i64类型的整数,就需要判断x是否满足非负数,因此创建一个
PositiveNonzeroInteger
类型的结构体,x的值作为结构体的值,然后调用结构体方法new() - 如果new成功返回Ok(PositiveNonzeroInteger(x as u64)),那么map_err()就返回这个Result::Ok类型的值
- 如果new出错了返回了一个Result::Err类型的值,那么map_err()就返回
ParsePosNonzeroError::from_creation
这个自定义的错误类型然后直接返回,而不是CreationError
generics2.rs
使用泛型
traits5.rs
特征的多重约束
quiz3.rs
关于格式化字符串format!:并不是所有的泛型都可以用这个函数,必须要有Display特征的泛型才可以,因此使用T: Display这个特征约束
pub fn notify<T: Summary>(item1: &T, item2: &T) {}
像这段代码的意思就是:约束T必须具有特征Summary,且item1和item2都是T类型的引用
lifetimes3.rs
关于结构体的生命周期:
不仅仅函数具有生命周期,结构体其实也有这个概念,只不过我们之前对结构体的使用都停留在非引用类型字段上。细心的同学应该能回想起来,之前为什么不在结构体中使用字符串字面量或者字符串切片,而是统一使用
String
类型?原因很简单,后者在结构体初始化时,只要转移所有权即可,而前者,抱歉,它们是引用,它们不能为所欲为。既然之前已经理解了生命周期,那么意味着在结构体中使用引用也变得可能:只要为结构体中的每一个引用标注上生命周期即可:
结构体
ImportantExcerpt
所引用的字符串 str
生命周期需要大于等于该结构体的生命周期。iterators2.rs
迭代器:
- 迭代器是会被消耗的:
当使用了next(),迭代器就会被消耗,相当于取出了那一个元素,那个元素其实已经不在迭代器里面了,因此最开始我直接将first转换为大写后直接返回了迭代器剩余的元素,这会导致返回的字符串没有预期的第一个字符
iterators4.rs
用迭代器实现递归:使用消费者适配器的方法
在 Rust 中,
(1..=num)
是一个范围(Range)表达式,表示从1到num
(包括num
本身)的一个范围。这个范围实际上是一个迭代器,它可以生成从1到num
的所有数字序列。在这种情况下,我们可以通过
(1..=num)
创建一个包含从1到num
的数字序列的迭代器。然后,我们调用迭代器的 product()
方法,这个方法将迭代器中的所有元素相乘,得到它们的乘积作为结果。因此,
(1..=num).product()
这个语句的意思是:先生成从1到num
的数字序列迭代器,然后计算这个序列中所有数字的乘积,最终得到阶乘的结果。iterators5.rs
关于闭包和迭代器方法的使用
不用循环,找出map里面有多少个键值为value的元素
.values()
:使用了HashMap的value方法,获取了一个包含所有键值的迭代器。
.filter()
:创建一个迭代器,该迭代器使用闭包确定是否应产生元素。给定一个元素,闭包必须返回true
或false
。返回的迭代器将仅生成闭包为其返回 true 的元素。
返回一个所有哈希表里面键值为指定状态的数量和:
.map()
:Iterator特征的函数,获取一个闭包并创建一个迭代器,该迭代器在每个元素上调用该闭包。
- 首先创建collection的迭代器,再使用map使得迭代器上的每个元素都调用count_iterator(),对于每个哈希表都计算键值为progress的个数
.sum()
:将迭代器里面的每个元素相加,所以这么写也是对的:
- Author:orangec
- URL:orange’s blog | welcome to my blog (clovy.top)/5b37b9bbc9134574a0283f07b0062e6a
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!