练习题来自:https://practice-zh.course.rs/collections/vector.html
1
fn main() {let arr: [u8; 3] = [1, 2, 3];let v = Vec::from(arr);is_vec(v);let v = vec![1, 2, 3];is_vec(v);// vec!(..) 和 vec![..] 是同样的宏,宏可以使用 []、()、{}三种形式,因此...let v = vec!(1, 2, 3);is_vec(v);// ...在下面的代码中, v 是 Vec<[u8; 3]> , 而不是 Vec<u8>// 使用 Vec::new 和 `for` 来重写下面这段代码let v1 = vec!(arr);is_vec(v1);assert_eq!(v, v1);println!("Success!")
}fn is_vec(v: Vec<u8>) {}
我真不觉得作者的意思是这么写,但是我只能这么写了。。
let v1 = Vec::new();let v = vec!{1, 2, 3};is_vec(v1);let mut v1:Vec<u8> = Vec::new();for e in &v{v1.push(*e);}assert_eq!(v, v1);println!("Success!")
另外吐槽Rust这个for循环,只有for each
格式的,没有三段式的。虽然for each
也能转成三段式吧。
C++使用vector
代表定长数组,据说是一个失误。但是Rust也这么搞,我倾向于是吸引从C++转来的程序员。这个宏定义需要加感叹号,感觉有点卡手。
2
// 填空
fn main() {let mut v1 = Vec::from([1, 2, 4]);v1.pop();v1.push(3);let mut v2 = Vec::new();v2.__;assert_eq!(v1, v2);println!("Success!")
}
extend
可以将vector
拼接到vector
的后面,如果C++实现类似的功能,需要用insert
,还需要指定迭代器,就这点来说Rust还不错。
vector<int> v1(a1,a1+3);vector<int> v2(a2,a2+3);v1.insert(v1.end(),v2.begin(),v2.end());
3
// 填空
fn main() {// array -> Vec// impl From<[T; N]> for Veclet arr = [1, 2, 3];let v1 = __(arr);let v2: Vec<i32> = arr.__();assert_eq!(v1, v2);// String -> Vec// impl From<String> for Veclet s = "hello".to_string();let v1: Vec<u8> = s.__();let s = "hello".to_string();let v2 = s.into_bytes();assert_eq!(v1, v2);// impl<'_> From<&'_ str> for Veclet s = "hello";let v3 = Vec::__(s);assert_eq!(v2, v3);// 迭代器 Iterators 可以通过 collect 变成 Veclet v4: Vec<i32> = [0; 10].into_iter().collect();assert_eq!(v4, vec![0; 10]);println!("Success!")}
这里其实和Vec
没多大关系,更多是在讲From
和Info
特征。
fn main() {// array -> Vec// impl From<[T; N]> for Veclet arr = [1, 2, 3];let v1 = Vec::from(arr);let v2: Vec<i32> = arr.into();assert_eq!(v1, v2);// String -> Vec// impl From<String> for Veclet s = "hello".to_string();let v1: Vec<u8> = s.into();let s = "hello".to_string();let v2 = s.into_bytes();assert_eq!(v1, v2);// impl<'_> From<&'_ str> for Veclet s = "hello";let v3 = Vec::from(s);assert_eq!(v2, v3);// 迭代器 Iterators 可以通过 collect 变成 Veclet v4: Vec<i32> = [0; 10].into_iter().collect();assert_eq!(v4, vec![0; 10]);println!("Success!")}
这两个特征的定义如下,用法都在上面了:
pub trait From<T>: Sized {// Required methodfn from(value: T) -> Self;
}pub trait Into<T>: Sized {// Required methodfn into(self) -> T;
}
需要注意的有几点:
- 绝大多数标准库都实现了这两个特征
- 实现了
From
会自动实现Into
- 由于
into
的结果可能有多种类型,你需要手动指定类型。 - Rust为
From
和Into
给出了一些原则,不过就目前的学习阶段来看,这些原则不是特别重要(比如转换必须是无损的)
4
// 修复错误并实现缺失的代码
fn main() {let mut v = Vec::from([1, 2, 3]);for i in 0..5 {println!("{:?}", v[i])}for i in 0..5 {// 实现这里的代码...}assert_eq!(v, vec![2, 3, 4, 5, 6]);println!("Success!")
}
有时候我觉得这题目挺cd的。
for i in 0..5 {if let Some(x) = v.get(i){v[i] = x+1;}else {//i=3,4v.push(i+2);}}
5
// 修复错误
fn main() {let mut v = vec![1, 2, 3];let slice1 = &v[..];// 越界访问将导致 panic.// 修改时必须使用 `v.len`let slice2 = &v[0..4];assert_eq!(slice1, slice2);// 切片是只读的// 注意:切片和 `&Vec` 是不同的类型,后者仅仅是 `Vec` 的引用,并可以通过解引用直接获取 `Vec`let vec_ref: &mut Vec<i32> = &mut v;(*vec_ref).push(4);let slice3 = &mut v[0..3];slice3.push(4);assert_eq!(slice3, &[1, 2, 3, 4]);println!("Success!")
}
答案
fn main() {let mut v = vec![1, 2, 3];let slice1 = &v[..];// 越界访问将导致 panic.// 修改时必须使用 `v.len`let slice2 = &v[0..v.len()];assert_eq!(slice1, slice2);// 切片是只读的// 注意:切片和 `&Vec` 是不同的类型,后者仅仅是 `Vec` 的引用,并可以通过解引用直接获取 `Vec`let vec_ref: &mut Vec<i32> = &mut v;(*vec_ref).push(4);let slice3 = &mut v[..];assert_eq!(slice3, &[1, 2, 3, 4]);println!("Success!")
}
你要是用VS Code之类的IDE就能看出来,Vec
切片的本质就是数组。当然,C++的vector
是没有切片这个功能的。
6
// 修复错误
fn main() {let mut vec = Vec::with_capacity(10);assert_eq!(vec.len(), __);assert_eq!(vec.capacity(), 10);// 由于提前设置了足够的容量,这里的循环不会造成任何内存分配...for i in 0..10 {vec.push(i);}assert_eq!(vec.len(), __);assert_eq!(vec.capacity(), __);// ...但是下面的代码会造成新的内存分配vec.push(11);assert_eq!(vec.len(), 11);assert!(vec.capacity() >= 11);// 填写一个合适的值,在 `for` 循环运行的过程中,不会造成任何内存分配let mut vec = Vec::with_capacity(__);for i in 0..100 {vec.push(i);}assert_eq!(vec.len(), __);assert_eq!(vec.capacity(), __);println!("Success!")
}
如果你用C++,那你对这里的size
和capacity
的区别应该不会陌生。写算法题时出于性能的考虑,往往需要提前分配足够的空间。
fn main() {let mut vec = Vec::with_capacity(10);assert_eq!(vec.len(), 0);assert_eq!(vec.capacity(), 10);// 由于提前设置了足够的容量,这里的循环不会造成任何内存分配...for i in 0..10 {vec.push(i);}assert_eq!(vec.len(), 10);assert_eq!(vec.capacity(), 10);// ...但是下面的代码会造成新的内存分配vec.push(11);assert_eq!(vec.len(), 11);assert!(vec.capacity() >= 11);// 填写一个合适的值,在 `for` 循环运行的过程中,不会造成任何内存分配let mut vec = Vec::with_capacity(100);for i in 0..100 {vec.push(i);}assert_eq!(vec.len(), 100);assert_eq!(vec.capacity(), 100);println!("Success!")
}
7
#[derive(Debug)]
enum IpAddr {V4(String),V6(String),
}
fn main() {// 填空let v : Vec<IpAddr>= __;// 枚举的比较需要派生 PartialEq 特征assert_eq!(v[0], IpAddr::V4("127.0.0.1".to_string()));assert_eq!(v[1], IpAddr::V6("::1".to_string()));println!("Success!")
}
又是为这种神奇的枚举惊叹的一次,这还能叫枚举吗?
#[derive(Debug, PartialEq)]
enum IpAddr {V4(String),V6(String),
}
fn main() {// 填空let v : Vec<IpAddr>= vec!{IpAddr::V4("127.0.0.1".to_string()), IpAddr::V6("::1".to_string())};// 枚举的比较需要派生 PartialEq 特征assert_eq!(v[0], IpAddr::V4("127.0.0.1".to_string()));assert_eq!(v[1], IpAddr::V6("::1".to_string()));println!("Success!")
}
8
trait IpAddr {fn display(&self);
}struct V4(String);
impl IpAddr for V4 {fn display(&self) {println!("ipv4: {:?}",self.0)}
}
struct V6(String);
impl IpAddr for V6 {fn display(&self) {println!("ipv6: {:?}",self.0)}
}fn main() {// 填空let v: __= vec![Box::new(V4("127.0.0.1".to_string())),Box::new(V6("::1".to_string())),];for ip in v {ip.display();}
}
C++的vector用法也是差不多的,父类作为声明,子类填入。
fn main() {// 填空let v:Vec<Box<dyn IpAddr>>= vec![Box::new(V4("127.0.0.1".to_string())),Box::new(V6("::1".to_string())),];for ip in v {ip.display();}
}