Rust学习:通过例子学Rust

文章目录

  • Rust官方练习:通过例子学Rust
    • 1. Hello World
      • 1.1 注释
      • 1.2 格式化输出
        • 1.2.1 调试(Debug)
        • 1.2.2 显示(Display)
        • 1.2.3 测试实例:List
        • 1.2.4 格式化
    • 2. 原生类型
      • 2.1 字面量和运算符
      • 2.2 元组
      • 2.3 数组和切片
    • 3. 自定义类型
      • 3.1 结构体
      • 3.2 枚举
        • 3.2.1 使用use
        • 3.2.2 C风格语法
        • 3.3.3 测试实例:链表
      • 3.3 常量
    • 4. 变量绑定
      • 4.1 可变变量
      • 4.2 作用域与遮蔽
      • 4.3 变量先声明
      • 4.4 冻结
    • 5. 类型系统
      • 5.1 类型转换
      • 5.2 字面量
      • 5.3 类型推断
      • 5.4 别名
    • 6. 类型转换
      • 6.1 From 和 Into
      • 6.2 TryFrom and TryInto
      • 6.3 ToString 和 FromStr
    • 7. 表达式
    • 8. 流程控制
      • 8.1 if / else
      • 8.2 loop循环
        • 8.2.1 嵌套循环和标签
        • 8.2.2 从loop循环返回
      • 8.3 while循环
      • 8.4 for循环
      • 8.5 match匹配
        • 8.5.1 解构
          • 8.5.1.1 元组
          • 8.5.1.2 枚举
          • 8.5.1.3 指针和引用
          • 8.5.1.4 结构体
        • 8.5.2 卫语句
        • 8.5.3 绑定@
      • 8.6 if let
      • 8.7 while let
    • 9. 函数
      • 9.1 方法
      • 9.2 闭包
        • 9.2.1 捕获
        • 9.2.2 作为输入参数
        • 9.2.3 类型匿名
        • 9.2.4 输入函数
        • 9.2.5 作为输出参数
        • 9.2.6 std中的例子
          • 9.2.6.1 Iterator::any
          • 9.2.6.2 Iterator::find
      • 9.3 **高阶函数**
      • 9.3 发散函数
    • 10. 模块
      • 10.1 可见性
      • 10.2 结构体的可见性
      • 10.3 use声明
      • 10.4 super 和 self
      • 10.5 文件分层
    • 11. crate
    • 12. cargo
      • 12.1 依赖
      • 12.2 约定规范
      • 12.3 测试
      • 12.4 构建脚本
    • 13. 属性
      • 13.1 死代码 dead_code
      • 13.2 crate
      • 13.3 cfg
        • 13.3.1 自定义条件
    • 14. 泛型
      • 14.4 trait bounds
      • 14.6 使用where进行约束
      • 14.8 关联类型用于trait
      • 14.9 虚类型实例
  • TO BE CONTINUED

Rust官方练习:通过例子学Rust

1. Hello World

1.1 注释

    // 使用 // 进行单行注释
    //

    // 也可以使用 /* */ 来进行块注释
    /* */  
    
    // 使用 /// 来进行文档注释
    /// 

1.2 格式化输出

fn main() {
   
    // format! 返回一个字符串 String类型的变量
    let name = vec!["Peter", "Trunph", "James"];

    // 并且,我们可以使用位置参数来进行格式化输出
    // 这将所有的后续输出数据当作一个数组使用
    // 如下例,将name[0], name[1], name[2] 视为一个数组
    // 则 {0} 表示这个数组的索引 0 的值,也就是 name[0]
    // 当然 {}内也可以不指定索引,则其默认从 0 开始,并且递增
    let str1 = format!("{0}-{1}-{2}", name[0], name[1], name[2]);
    let str2 = format!("{}-{}-{}", name[0], name[1], name[2]);
    println!("{}", str1);
    println!("{}", str2);
    println!("****************************");

    // 并且, format还可以传递同作用域的变量
    // 如果在其参数列表中存在相同名字的数据,则优先使用参数列表
    let people = "Rustaceans";
    let disv1 = format!("Hello {people}!");
    let disv2 = format!("Hello {people}!", people = "Killer");
    println!("{}", disv1);
    println!("{}", disv2);
    println!("****************************");

    // println! 将文本输出到终端( print!和其作用相同,只不过该宏不进行换行)
    print!("我不换行了--");
    println!("我得换个行--");
    println!("****************************");

    // 我们在 format说明时使用了位置参数进行数据的传递
    // 除了位置参数,还可以使用命名参数
    println!(
        "{subject} {verb} {object}",
        object = "the lazy dog",
        subject = "the quick brown fox",
        verb = "jumps over"
    );
    println!("****************************");

    // 那么如何进行其他格式化的输出呢,比如进制转换,对齐,保留小数点等
    // 可以在 `:` 后面指定特殊的格式。
    // {:b} 表示把传入的数据转换为二进制输出,看下面这个栗子
    // 它把 2 转换为二进制 10 进行输出
    println!("{} of {:b} people know binary, the other half don't", 1, 2);
    println!("****************************");

    // 你可以按指定宽度来右对齐文本。
    // 下面语句输出 "     1",5 个空格后面连着 1。
    // 同理,你也可以左对齐文本
    println!("{number:, number = 1, width = 6);
    println!("{number:>width$}", number = 1, width = 6);
    println!("****************************");
    // 并且你可以使用符号 0 来让 0 占有空位置
    // 注意这是在左边补 0 ,和左对齐右对齐无关
    println!("{number:>0width$}", number = 1, width = 6);
    println!("{number:<0width$}", number = 1, width = 6);
    println!("****************************");
    // 更简便的,你可以不使用命名参数,直接使用如下方式
    // 这表示共计占有 4 位宽度,左边用 0 补齐
    println!("{:04}", 5);
    println!("****************************");

    // 保留特定位小数点
    let pi = 3.141592;
    // 我们想让 pi 保留三位小数点输出
    // 方式如下:冒号前面的是位置参数,他代表参数中的索引 0
    // 冒号后面我们使用一个 . ,再加上要保留的位数即可
    // {0:.3} 表示保留三位小数点
    println!("Pi is roughly {0:.3}", pi);
    println!("****************************");

    // 还有另外一种使用位置参数来定义位数的
    // 这代表使用参数索引 1 的值来作为宽度
    // 并且,默认的数据索引可以省略不写
    println!("{:1$}", 0, 2);
    println!("****************************");
    // 使用命名参数的表示方法同理
    println!("{:>width$}", 10, width = 6);
    println!("****************************");

    // 最后我们看一下本节的习题
    // 用一个 println! 宏,通过控制显示的小数位数来打印:Pi is roughly 3.142
    //(Pi 约等于 3.142)。为了达到练习目的,使用 let pi = 3.141592 作为 Pi 的近似值
    // 其实我们在上面已经实现了,不妨再做一遍加深印象
    let pi = 3.141592;
    println!("Pi is roughly {0:.3}", pi);

    // 本节练习地址
    // https://rustwiki.org/zh-CN/rust-by-example/hello/print.html

    // 出去上述说明的格式化方式,还有许多其他的格式化方式
    // 如输出 0x55这种十六进制格式的方式等等,以及重要的 debug方式
    // 可以到下面的官方标准库网站进行查阅
    // https://rustwiki.org/zh-CN/std/fmt/
}
1.2.1 调试(Debug)
#[derive(Debug)]
enum Gender {
   
    Male,
    Female,
}

#[derive(Debug)]
struct Student {
   
    grade: f64,
    height: u32,
    gender: Gender,
    hobby: String,
}

fn main() {
   
    // 并不是所有的类型都能够使用std::fmt的格式进行打印
    // 想要能够进行这样的打印,需要实现一个可以打印的 trait
    // 那么我们该如何输出那些不能进行 fmt::Display的类型呢
    // 答案就是使用fmt::Debug进行实现
    // 我们需要导入一个 Attribute,就是 derive 这个属性
    // 他会对标签进行自动的推导
    // 我们在程序外部导入 derive 对 fmt::Debug 进行自动推导
    // 然后我们定义一个结构体 Student,并且进行实例化
    let peter = Student {
   
        grade: 95.0,
        height: 175,
        gender: Gender::Male,
        hobby: String::from("Play LOL"),
    };

    let jane = Student {
   
        grade: 94.5,
        height: 165,
        gender: Gender::Female,
        hobby: String::from("Reading"),
    };

    // 下面我们使用 printt!宏来打印 这两个实例
    // 我们会发现他并不能进行打印,因为他没有实现 Display
    // println!("{}, {}", peter, jane);

    // 下面我们使用 Debug的方式进行打印
    println!("{:#?}\n{:#?}", peter, jane);
    // 输出信息如下
    /*
        Student {
            grade: 95.0,
            height: 175,
            gender: Male,
            hobby: "Play LOL",
        }
        Student {
            grade: 94.5,
            height: 165,
            gender: Female,
            hobby: "Reading",
        }
     */ 
}
1.2.2 显示(Display)
use std::fmt;

#[derive(Debug)]
enum Gender {
   
    Male,
    Female,
}

// 为 Gender实现一个方法,这个方法可以被调用返回他的字符串切片
impl Gender {
   
    fn get_str(&self) -> &str {
   
        match *self {
   
            Gender::Male => "Male",
            Gender::Female => "Female",
        }
    }
}

#[derive(Debug)]
struct Student {
   
    grade: f64,
    height: u32,
    gender: Gender,
    hobby: String,
}

// 为 Student实现 Display
impl fmt::Display for Student {
   
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
   
        write!(
            f,
            "grade is {}, height is {}, gender is {}, hobby is {}",
            self.grade,
            self.height,
            self.gender.get_str(),
            self.hobby
        )
    }
}

// 习题练习
#[derive(Debug)]
struct Point2D {
   
    x: f64,
    y: f64,
}

// 对 `Point2D` 实现 `Display`
impl fmt::Display for Point2D {
   
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
   
        // 自定义格式,使得仅显示 `x` 和 `y` 的值。
        write!(f, "{} + {}i", self.x, self.y)
    }
}

fn main() {
   
    // 在 1.2.1 中,我们使用 derive 属性对 Debug 进行推导进行打印
    // 但是它不够简洁,我们可以自己来实现 fmt::Display方法
    // 首先引入fmt模块
    // 然后我们把上个例子的结构体拿过来
    // 之后,我们为 struct和 enum实现 Display这个 trait
    // 见上方的实现
    // 定义完成后,我们来打印一下
    let peter = Student {
   
        grade: 95.0,
        height: 175,
        gender: Gender::Male,
        hobby: String::from("Play LOL"),
    };

    let jane = Student {
   
        grade: 94.5,
        height: 165,
        gender: Gender::Female,
        hobby: String::from("Reading"),
    };

    println!("{}", peter);
    println!("{}", jane);
    println!("{:#?}", peter);
    println!("{:#?}", jane);

    // 打印结果如下,可见,我们成功为struct实现了Display这个trait
    /*
       grade is 95, height is 175, gender is Male, hobby is Play LOL
       grade is 94.5, height is 165, gender is Female, hobby is Reading
       Student {
           grade: 95.0,
           height: 175,
           gender: Male,
           hobby: "Play LOL",
       }
       Student {
           grade: 94.5,
           height: 165,
           gender: Female,
           hobby: "Reading",
       }
    */
    // 可以看到,我们自己实现的Display和 Debug的输出方式明显不同

    // 最后我们来看一下这一节的习题
    let point = Point2D {
    x: 3.3, y: 7.2 };
    // 使用我们定义的 Display打印
    println!("{}", point);
    // 使用Debug打印
    println!("{:#?}", point);
    // 输出结果如下,目标完成
    /*
        3.3 + 7.2i
        Point2D {
            x: 3.3,
            y: 7.2,
        }
    */
}
1.2.3 测试实例:List
use ::std::fmt;

// 定义一个元组结构体
struct List(Vec<i32>);

// 为其实现Display这个trait
// impl fmt::Display for List {
   
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
   
//         let vec = &self.0;
//         write!(f, "[")?;
//         for (count, item) in vec.iter().enumerate() {
   
//             if count != 0 {
   
//                 write!(f, ", ")?;
//             }
//             write!(f, "{}", item)?;
//         }
//         write!(f, "]")
//     }
// }

impl fmt::Display for List {
   
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
   
        let vec = &self.0;
        write!(f, "[")?;
        for (count, item) in vec.iter().enumerate() {
   
            if count != 0 {
   
                write!(f, ", ")?;
            }
            write!(f, "{}: {}", count, item)?;
        }
        write!(f, "]")
    }
}

fn main() {
   
    // 这节来学习一下 ?符号的使用
    // ? 操作符会自动进行match匹配,如果错误则会返回,没有错误则继续
    let v = List(vec![1, 2, 3]);
    println!("{}", v);
    // 输出如下
    // [1, 2, 3]
    // 可见,使用 ? 可以省略我们很多的重复语句

    // 下面我们来看一下本节的习题

    //更改程序使 vector 里面每个元素的下标也能够打印出来。新的结果如下:
    //[0: 1, 1: 2, 2: 3]

    // 我们先将之前的注释掉
    // 改完之后,输出一下试试
    println!("{}", v);
    // 结果如下
    // [0: 1, 1: 2, 2: 3]
    // 目标达成
}
1.2.4 格式化
use std::fmt::{
   self, Display};

// 定义一个结构体
struct City {
   
    name: &'static str,
    lat: f32,
    lon: f32,
}

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

// 为City实现Display
impl Display for City {
   
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
   
        let lac_c = if self.lat > 0.0 {
    'N' } else {
    'S' };
        let lon_c = if self.lon > 0.0 {
    'E' } else {
    'W' };

        write!(
            f,
            "{}: {:3}°{} {:3}°{}",
            self.name,
            self.lat.abs(),
            lac_c,
            self.lon.abs(),
            lon_c
        )
    }
}

// 习题:为Color实现Display
impl Display for Color {
   
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
   
        write!(
            f,
            "RGB ({0}, {1}, {2}) 0x{0:02X}{1:02X}{2:02X}",
            self.red, self.green, self.blue
        )
    }
}

fn main() {
   
    // 之前我们介绍format!等宏时说到
    // 在 {} 中加入 :param 可以改变输出的格式
    // 这个功能是使用 trait来实现的
    // 下面我们来自己实现一下这个trait
    for city in [
        City {
   
            name: "Dublin",
            lat: 53.347778,
            lon: -6.259722,
        },
        City {
   
            name: "Oslo",
            lat: 59.95,
            lon: 10.75,
        },
        City {
   
            name: "Vancouver",
            lat: 49.25,
            lon: -123.1,
        },
    ]
    .iter()
    {
   
        println!("{}", *city);
    }
    // 输出结果如下
    // Dublin: 53.34778°N 6.259722°W
    // Oslo: 59.95°N 10.75°E
    // Vancouver: 49.25°N 123.1°W

    // 下面我们来看一下本节习题
    // 为上面的 Color 结构体实现 fmt::Display,应得到如下的输出结果:
    // RGB (128, 255, 90) 0x80FF5A
    // RGB (0, 3, 254) 0x0003FE
    // RGB (0, 0, 0) 0x000000
    for color in [
        Color {
   
            red: 128,
            green: 255,
            blue: 90,
        },
        Color {
   
            red: 0,
            green: 3,
            blue: 254,
        },
        Color {
   
            red: 0,
            green: 0,
            blue: 0,
        },
    ]
    .iter()
    {
   
        // 在添加了针对 fmt::Display 的实现后,请改用 {} 检验效果。
        println!("{}", *color)
    }

    // 结果如下
    /*
        RGB (128, 255, 90) 0x80FF5A
        RGB (0, 3, 254) 0x0003FE
        RGB (0, 0, 0) 0x000000
    */
    // 任务完成

    // 虽然我们任务完成了,但是离我们开头说的实现{:param}这种实现形式
    // 还是差了很多,那么我们怎么实现这种格式的输出呢
    // 首先我们定义一个结构体,它表示一个浮点数 x.y
    // x是他的整数部分,y是他的小数部分,并且我们要求它能够实现
    // {:param}可以指定它小数输出部分的位数
    // 下面我们来实现它
    let flo = Flo {
    x: 18, y: 52 };
    println!("{:10.04}", flo);
    // 我们要求是将flo结构体中的x作为整数,y作为小数
    // 合并成一个数进行输出,并且可以指定小数的位数
    // 上述例子将会输出   18.5200
    // 示例成功
    // 读者如果想要实现其他不同的功能与输出方式
    // 请阅读 https://rustwiki.org/zh-CN/std/fmt/#formatting-traits
    // 来寻找自己想要的方法,博主才疏学浅,就不过多说明了
}

struct Flo {
   
    x: i32,
    y: u32,
}

impl fmt::Display for Flo {
   
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
   
        let num: f64 = format!("{}.{}", self.x, self.y).parse().unwrap();
        let decimals = f.precision().unwrap_or(3);
        let string = format!("{:.*}", decimals, num);
        f.pad_integral(true, "", &string)
    }
}

2. 原生类型

fn main() {
   
    // Rust中的那些原生类型
    // 标量类型
    // 有符号整数
    let itype1: i8 = 8;
    let itype2: i16 = 16;
    let itype3: i32 = 32;
    let itype4: i64 = 64;
    let itype5: i128 = 128;
    let itype6: isize = 64; // 指针宽度,32位或64位

    // 无符号整数
    let utype1: u8 = 8;
    let utype2: u16 = 16;
    let utype3: u32 = 32;
    let utype4: u64 = 64;
    let utype5: u128 = 128;
    let utype6: usize = 64; // 指针宽度,32位或64位

    // 也可以使用后缀声明变量类型
    let utype_suffix = 50i32;

    // 浮点数
    let ftype1: f32 = 32.00;
    let ftype2: f64 = 64.00;

    // 布尔型
    let bool_type: bool = true; // true or false

    // 单元类型:空元组
    let unit_type = ();

    // 复合类型
    // 数组
    let array_type = [1, 2, 3, 4, 5];

    // 元组
    let tuple_type = (1, 2, 3, 4, 5);

    // 定义一个变量时,如果不显示声明变量的可变性,则变量默认不可变
    // 使用mut来使变量可变
    let mut mutable = 56;

    // 变量的值可以改变,但是变量的类型不能改变
    // 但是你可以使用shadow来遮蔽前面的变量
    // mutable = true;
    let mutable = true;
}

2.1 字面量和运算符

fn main() {
   
    // 整数相加
    println!("1 + 2 = {}", 1u32 + 2);

    // 整数相减
    println!("1 - 2 = {}", 1i32 - 2);
    // 试一试 ^ 尝试将 `1i32` 改为 `1u32`,体会为什么类型声明这么重要

    // 短路求值的布尔逻辑
    println!("true AND false is {}", true && false);
    println!("true OR false is {}", true || false);
    println!("NOT true is {}", !true);

    // 位运算
    println!("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101);
    println!("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101);
    println!("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101);
    println!("1 << 5 is {}", 1u32 << 5);
    println!("0x80 >> 2 is 0x{:x}", 0x80u32 >> 2);

    // 使用下划线改善数字的可读性!
    println!("One million is written as {}", 1_000_000u32);
}

2.2 元组

use std::fmt;

// 元组可以充当函数的参数和返回值
fn reverse(pair: (i32, bool)) -> (bool, i32) {
   
    // 可以使用 `let` 把一个元组的成员绑定到一些变量
    let (integer, boolean) = pair;
    (boolean, integer)
}

// 在练习中要用到下面这个结构体。
#[derive(Debug)]
struct Matrix(f32, f32, f32, f32);

fn main() {
   
    // 包含各种不同类型的元组
    let long_tuple = (1u8, 2u16, 3u32, 4u64,
                      -1i8, -2i16, -3i32, -4i64,
                      0.1f32, 0.2f64

你可能感兴趣的:(Rust,rust)