Table of Contents
为了在 rustlings 里面用 rust analyzer,可以运行 rustlings lsp 命令,这样会生成 rust-project.json 文件提供一些 crate 信息,lsp 就可以使用了~
这里记录一下写的不是很优雅的地方可以怎么改进,以及一些理解。
while let 用来遍历链表非常合适
while let 很适合用于完成遍历数组的工作,比如
while let (Some(node_a), Some(node_b)) = (current_a, current_b) { let val_a = unsafe { &(*node_a.as_ptr()).val }; let val_b = unsafe { &(*node_b.as_ptr()).val }; ...相比起 cpp 可能会用到的写法,含义上清晰许多呢,while let 就暗含了“只遍历 Some 的情况,不包括 None”
while(p) { ...}match 可以用来做一些格式的匹配
为了从形如 "Gerald,70" 的字符串中提取出名字与年龄,需要做的判断包括,字符串非空,逗号前后都要有内容,否则返回 Person::default(),如果挨个用 if 判断的方式是这样:
impl From<&str> for Person { fn from(s: &str) -> Person { // 检查非空 if s.len() == 0 { return Person::default(); }
let sv: Vec<_> = s.split(",").collect(); // 只有一个 "," if sv.len() != 2 { return Person::default(); } let number = sv[1].parse::<usize>(); if sv[0] == "" || number.is_err() { Person::default() } else { Person { name: sv[0].to_string(), age: number.unwrap(), } } }}可以用 match 来完成这些工作,还可以加上卫语句(if 判断) 来过滤分支。
match s.split(",").collect::<Vec<&str>>().as_slice() { &[name, age] if !name.is_empty() && !age.is_empty() => {}, _ => return Person::default(), }此外这里有一个 clippy 提示,is_empyty() 的比较效率更高。Clippy Lints
if s == "" {可以优化成if s.is_empyty() {所以总体可以写成这个样子,不过这么写的不是很好的地方是就是不好体现错误信息了…
impl From<&str> for Person { fn from(s: &str) -> Person { // as_slice 返回 &[&str] 切片的引用 match s.split_once(',') { Some((name, age)) if !name.is_empty() && !age.is_empty() => { if let Ok(number) = age.parse::<usize>() { Person { name: name.to_string(), age: number, } } else { Person::default() } } _ => Person::default(), } }}除了 filter 也可以过滤一些 option,unwrap_or_default() 来返回默认值。
impl From<&str> for Person { fn from(s: &str) -> Person { s.split_once(',') .filter(|(name, age)| !name.is_empty() && !age.is_empty()) .and_then(|(name, age)| age.parse::<usize>().ok().map(|age_num| (name, age_num))) .map(|(name, age_num)| Person { name: name.to_string(), age: age_num, }) .unwrap_or_default() }}错误处理的时候组合器很好用
如果是出需要返回不通的错误值的时候,组合器很好用的。
impl FromStr for Person { type Err = ParsePersonError; fn from_str(s: &str) -> Result<Person, Self::Err> { if s.is_empty() { return Err(ParsePersonError::Empty); }
match s.split(",").collect::<Vec<_>>().as_slice() { &[name, age] if name.is_empty() => Err(ParsePersonError::NoName), &[name, age] => age .parse::<usize>() .map(|number| Person { name: name.to_string(), age: number, }) .map_err(|e| ParsePersonError::ParseInt(e)),
_ => Err(ParsePersonError::BadLen), } }}要去理解这些组合器是什么意思可以去看 clippy 里面的 manual_xxx 的部分,比如 ok 和 errr
clippy 提醒需要避免的写法是,实际上也是解释了 ok 和 err 的作用
let a = match func() { Ok(v) => Some(v), Err(_) => None,};let b = if let Err(v) = func() { Some(v)} else { None};相应的 clippy 建议的写法是:
let a = func().ok();let b = func().err();NonNull
双向链表并不是使用 Box 而是使用了 NonNull 来包裹,是因为 Box 的所有权是独占的,无法直接表示共享所有权或循环结构(如双向链表)。而且 nonnull 性能好点,。
struct Node<T> { val: T, next: Option<NonNull<Node<T>>>,}还有一些 vec 的方法
堆排序中,从 idx=1 位置取出一个元素并用末尾的元素替换 idx 1 的元素,这个操作可以用 swap_remove 完成
fn next(&mut self) -> Option<T> { if self.is_empty() { return None; }
// 最后一个元素移动到 idx 1 位置 let result = self.items.swap_remove(1); self.count -= 1;
let mut idx = 1; while self.children_present(idx) {.... } Some(result) }关于修改 hashmap 里面的元素
可以用 get mut 来修改
if let Some(v) = self.adjacency_table_mutable().get_mut(p) { v.push((q.to_string(), e)) }也可以用 entry and_modify。emmmm 不过这两种,好像差不多?
self.adjacency_table .entry(edge.1.to_owned()) .and_modify(|edges| edges.push((edge.0.to_owned(), edge.2)));循环的事情交给 iter
在写的时候感觉 some count 这些需要特地换成 iter 很不习惯,想想好像也合适,毕竟是循环…