一、 准备

项目地址
搭建环境:使用Linux+VS Code在Win上远程开发

Rust环境配置

在终端中打开输入

curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

如果提示curl无法识别就装一下curl
测试:在存放项目的文件夹中打开终端,运行cargo new first_project,会在该目录下创建一个项目,cd到first_project中,输入

cargo run

如果出现Hello, world1!则说明Rust环境配置成功

VS Code远程连接

  尝试了三种远程方式,远程桌面、向日葵和VS Code远程,远程桌面延迟是最高的,完成没法用。向日葵延迟比较低,但是由于无法适应屏幕,竖直的1080pLinux投到4k27寸的屏上字体非常小,最后还是感觉VS Code远程开发最舒服。
首先在两个系统上都装上VS Code,并且在插件管理中下载Remote-ssh。同时在Linux终端中下载ssh

apt-get install openssh-server

然后输入启动命令

ssh-server:/etc/init.d/ssh/ssh start

需要注意/etc/init.d/ssh 这是路径名,可能会不一样,并且如果是在桌面打开中端的记得cd …回到根目录

然后在Linux中查看自己的IP,一般在设置->网络中能看到,或者使用命令

ip addr

  然后在win的VS Code中点击刚刚下载的插件,名称较远程资源管理器,并且输入Linux的地址,输入密码即可连接成功。如果出现错误:视图连接的管道不存在可以看看Linux上的ssh安装成功没有,有没有启动,重启命令

/etc/init.d/ssh/ssh restart

将项目克隆下来并在VS Code中打开,终端中输入

cargo test

如果出现下面的内容则说明环境搭建成功
运行结果

二、Rust练习项目

这里建议将Rust的练习项目单独拿出来。
首先使用命令创建一个不带默认文件的项目

cargo new rustlings_solutions --lib

然后再src下创建文件夹exercises,将原项目中rustlings_exercises中的文件剪切到这个目录下。然后在lib.rs中,删除默认代码,添加代码:

pub mod exercises {
    pub mod generics2;
    pub mod move_semantics1;
    pub mod move_semantics2;
    pub mod move_semantics3;
    pub mod options1;
    pub mod primitive_types4;
    pub mod structs1;
    pub mod tests3;
    pub mod vecs2;
}

并运行cargo test,能成功运行即可。

1. generics2.rs

这个练习是需要使用泛型来重写Wrapper结构,使其能够包装任何类型的值,类似于template<class T>这样
代码如下:

struct Wrapper<T> {
    value: T,
}

impl<T> Wrapper<T> {
    pub fn new(value: T) -> Self {
        Wrapper { value }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn store_u32_in_wrapper() {
        assert_eq!(Wrapper::new(42).value, 42);
    }

    #[test]
    fn store_str_in_wrapper() {
        assert_eq!(Wrapper::new("Foo").value, "Foo");
    }
}

2. move_semantics1.rs

这个联系要求添加mut关键字使得vec变量可变,从而能够添加新的元素,由于Rust默认将函数参数作为不可变引用,因此不能在函数内部修改它
代码如下:

#[test]
fn main() {
    let vec0 = vec![22, 44, 66];

    let vec1 = fill_vec(vec0);

    assert_eq!(vec1, vec![22, 44, 66, 88]);
}

fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {
    vec.push(88);
    
    vec
}

3. move_semantics2.rs

这个练习要求将两个Vec分开,在Rust中,将一个值传递给函数时,它默认会被移到到函数内部,也就是说在原函数中,后面就无法再使用这个值了,因此可以克隆传副本或者参数的借用(下一个练习的内容)
代码如下:

#[test]
fn main() {
    let vec0 = vec![22, 44, 66];

    let vec1 = fill_vec(vec0.clone());

    assert_eq!(vec0, vec![22, 44, 66]);
    assert_eq!(vec1, vec![22, 44, 66, 88]);
}

fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
    let mut vec = vec;

    vec.push(88);

    vec
}

4. move_semantics3.rs

使用另一种方式解决上一个问题
代码如下:

#[test]
fn main() {
    let vec0 = vec![22, 44, 66];

    let vec1 = fill_vec(&vec0);

    assert_eq!(vec1, vec![22, 44, 66, 88]);
}

fn fill_vec(vec: &Vec<i32>) -> Vec<i32> {
    let mut vec = vec.clone();
    vec.push(88);
    vec
}

5. options1.rs

没什么特点,写一个判断函数,并修改测试函数
代码如下:

fn maybe_icecream(time_of_day: u16) -> Option<u16> {
    if time_of_day > 23 {
        None
    } 
    else if time_of_day >= 22 {
        Some(0)
    } 
    else {
        Some(5)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn check_icecream() {
        assert_eq!(maybe_icecream(9), Some(5));
        assert_eq!(maybe_icecream(10), Some(5));
        assert_eq!(maybe_icecream(23), Some(0));
        assert_eq!(maybe_icecream(22), Some(0));
        assert_eq!(maybe_icecream(25), None);
    }

    #[test]
    fn raw_value() {
        let icecreams = maybe_icecream(12);
        match icecreams {
            Some(inner_value) => {
                assert_eq!(inner_value, 5);
            }
            None => {
                println!("There is no value.");
            }
        }
    }
}

6. primitive_types4.rs

数组切片
代码如下:

#[test]
fn slice_out_of_array() {
    let a = [1, 2, 3, 4, 5];

    let nice_slice = &a[1..4];

    assert_eq!([2, 3, 4], nice_slice)
}

7. structs1.rs

实现三种形式的结构体,元组结构体(Tuple Structs)、经典结构体(Classic Structs)和单元结构体(Unit Structs),前两种比较好理解,单元结构体是一种特殊的结构体,它不包含任何字段,不需要存储数据。
代码如下:

struct ColorClassicStruct {
    red: u8,
    green: u8,
    blue: u8,
}

struct ColorTupleStruct(u8, u8, u8);

#[derive(Debug)]
struct UnitLikeStruct;

#[cfg(test)]
mod tests {
use super::*;

    #[test]
    fn classic_c_structs() {
        let green = ColorClassicStruct {
            red: 0,
            green: 255,
            blue: 0,
        };

        assert_eq!(green.red, 0);
        assert_eq!(green.green, 255);
        assert_eq!(green.blue, 0);
    }

    #[test]
    fn tuple_structs() {
        let green = ColorTupleStruct(0, 255, 0);

        assert_eq!(green.0, 0);
        assert_eq!(green.1, 255);
        assert_eq!(green.2, 0);
    }

    #[test]
    fn unit_structs() {
        let unit_like_struct = UnitLikeStruct;
        let message = format!("{:?}s are fun!", unit_like_struct);

        assert_eq!(message, "UnitLikeStructs are fun!");
    }
}

8. tests3.rs

实现判断是否是偶数
代码如下:

pub fn is_even(num: i32) -> bool {
    num % 2 == 0
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn is_true_when_even() {
        assert!(is_even(4));
    }

    #[test]
    fn is_false_when_odd() {
        assert!(!is_even(5));
    }
}

9. vecs2.rs

完成循环以使每个向量中的数字乘以2,要求使用两种方式实现,iter_mut()和iter()+map()
第一种是原地修改,直接修改原向量,而第二种则是备份修改,返回 的是一个新的vec,而原始向量不会发生改变
代码如下:

fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
    for element in v.iter_mut() {
        *element *= 2;
    }

    v
}

fn vec_map(v: &Vec<i32>) -> Vec<i32> {
    v.iter().map(|&element| element * 2).collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_vec_loop() {
        let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
        let ans = vec_loop(v.clone());

        assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
    }

    #[test]
    fn test_vec_map() {
        let v: Vec<i32> = (1..).filter(|x| x % 2 == 0).take(5).collect();
        let ans = vec_map(&v);

        assert_eq!(ans, v.iter().map(|x| x * 2).collect::<Vec<i32>>());
    }
}

更多推荐