初学Rust——Enums and Modules and Collections

今天是学习rust的第三天,学习材料为官网的《The Rust Programming Language》今日内容包括第六章:枚举enums、第七章:Packages和第八章:Collections

Chapter 6 Enums and Pattern Matching

6.1. Defining an enums

为了避免错误,rust语言没有null值定义,但有一个枚举来实现null的功能——定义在标准库中的Option<T>

enum Option<T> {
    Some(T),
    None,
}

鉴于其常用性,不需要在使用时显示声明enum Option,同样可以直接使用Some或None而不需要写Option::Some。定义中的<T>表示该枚举可以适用于所有类型:

let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None; //若是使用None则要声明一下你在使用什么数据类型

由于Option<T>的引入,Option<T>和T不是同一种数据类型,因而不需担心数值是null而引发错误的问题,从而增强了rust代码的安全性。

6.2. The Match Control Flow Operator

基于代码的运行结果控制程序的走向

enum Coin {   //定义一个结构体
    Penny,
    Nickel,
    Dime,
    Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,  //match arm:pattern+code,code的部分可以用{}来组织更为复杂的代码块
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

match与Option<T>:

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);

*注意枚举类的match必须穷尽所有可能,不然rust会报错!*当我们不想列举所有情况时,想要省略的情况可以用占位符_来代替,如:

let some_u8_value = 0u8;
match some_u8_value {
    1 => println!("one"),
    3 => println!("three"),
    5 => println!("five"),
    7 => println!("seven"),
    _ => (),  //占位符
}

6.3 Concise Control Flow with if let

那么如果我们只想对枚举类型中的某一个情况进行处理时,可以使用if let

if let Some(3) = some_u8_value {  //注意此时rust不再有穷尽检查,用户需要自己来保证控制流的正确性!
    println!("three");
}

if let同样可以与else搭配使用~

Chapter 7, Managing Growing Projects with Packages, Crates and Modulars

rust提供了多种功能帮助程序媛管理代码,有时称这些工具为module system,包括:

  • Packages:cargo feature,能够build、test、share crates
  • Crates:树形modules,产生库或可执行块
  • Modules and use:控制organization、scope和privacy of paths
  • Paths:命名item的方法,比如struct、function、module

7.1 Packages and Crates

crate:binary或library
crate root:rust编译器开始的源文件
package:提供一系列功能的一个或多个crates,一个package包含一个Cargo.toml文件,描述了如何build这些crates。

package必须包含0个或1个library crates,可以包含任意数量的binary crates,但是不能为空。

输入命令:

$ cargo new my-project
     Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs

当我们创建一个my-project时,cargo创建了一个Cargo.toml文件,生成一个package。进入Cargo.toml文件时,可以看到:

[package]
name = "rust"
version = "0.1.0"
authors = ["MY_NAME"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

注意这里没有提到src/main.rs文件,因为cargo遵循“src/main.rs”为binary crate的crate root,与package有相同的名字。类似的,如果package包含一个src/lib.rs,cargo就知道这个package包含一个library crate,与package邮箱的名字,在编译时会将root file交给rustc

一个package可以有多个binary crates,放在src/bin下。

7.2 Defining Modules to Control Scope and Privacy

modules帮助我们在crate内组织代码,构成群组,具备一定的可读性并且方便实用。modules还控制项目的privacy,即一个item是否可以被代码外的程序段调用(public)还是仅能够在内部使用(private)。
以一个饭店crate的搭建为例(仅给出结构,没有具体的实现细节)

以下代码:

mod front_of_house {   //关键字 mod
    mod hosting {      //模块内部可以有别的模块
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }
    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

通过模块,我们可以构建起相关的定义,并且标识他们为什么相关。程序媛可以简单地通过groups去找到想要使用的定义,而不用详细的读所有的定义;添加代码也同样更清晰。
module tree

crate
 └── front_of_house
     ├── hosting
        ├── add_to_waitlist
        └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment

src/mainsrc/lib作为module tree的根。

7.3 Paths for Referring to an Item in the Module Tree

与文件系统很像,为了指明模块的位置,我们要给出模块的路径。

  • 绝对路径:Absolute path:从crate root开始
  • 相对路径:Relative path:从当前模块开始,使用self super或当前模块中的identifier

路径中的间隔使用符号::

module还能帮助我们定义rust的privacy,注意rust中各种items默认是private的。parent节点不能使用child的功能,但是child可以使用其祖先的功能。

Exposing Paths with the pub Keyword

加入 pub 关键字可以将默认private 的item变成public,从而可以访问

Starting Relative Paths with super

super关键字相当于文件系统的.. 回到上层

Making Structs and Enums Public

对于Structs结构体,当我们将结构体public,其内部变量依然是private的,需要我们逐个进行public,不作操作的内部变量依然会保持private;
对于Enums枚举,我们在enum前添加pub关键字,则整个结构体全部变为public。

7.4 Bring Paths into Scope with the use Keyword

使用use关键字,避免代码冗长

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
use crate::front_of_house::hosting;  //use关键字,将路径引入
pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}
fn main() {}

Creating Idiomatic use Path

通常习惯下,我们将要使用的函数的父节点使用use进行引入,而不是直接引入函数,这样在使用的时候加上:: 可以有效地表明这个函数来自别处的定义,不是本地定义。

在使用use时,习惯写明完整的地址。

Providing New Names with the as Keyword

as重命名,避免重名的尴尬(此处有些像python的import … as …)

Re-exporting Names with pub use

pub use关键字使得我们可以既使用其他代码的模块,又让该模块继续被其他模块调用,所以称为re-exporting。

Using External Packages

crates.io 下载外部依赖,写在 C a r g o . t o m l Cargo.toml Cargo.toml内的[denpendencies]下。代码中通过use 关键字来调用

Using Nested Paths to Clean Up Large use Lists

代码

use std::io;
use std::cmp::Ordering;
// ---snip---

与代码

use std::{cmp::Ordering, io};
// ---snip---

等价

The Glob Operator

使用符号*来引入所有功能,与python的import * 相同

7.5 Separating Modules into Different Files

Chapter 8: Common Collections

collections可以容纳多个值,与构建在矩阵中的元组不同,这些collections指向的数据存在堆(heap)中,即元素的长度在编译时是不知道的,可以随着程序的进程而改变。本章内容包括:

  • Vector
  • String
  • Hash map

8.1 Storing Lists of Values with Vectors

使得程序媛可以在一个数据结构中存放多个值,数据在内存中相邻存放。vector中存储的数据必须是相同的类型。具体内容见代码笔记。

8.2 Storing UTF-8 Encoded Text with Strings

在rust的core language中,只定义了一种“string”,为string slice str,常见为&str。String 类型是在rust的标准库中提供的,并不在core language中。这两种类型都是UTF-8编码的。

注意String类是不支持下标查询的!这是由于String本身所支持的UTF-8编码导致的。每个UTF-8编码的字符不一定是合法的character。还有一个原因:rust不能保证indexing operation总是在O(1)时间内完成,因为总是要遍历之前的所有元素来确定String中有多少个合法字符。

由于index的方法不能指明返回的到底是a byte value, a character, a grapheme cluster, 还是a string slice,因而是一个不好的操作。rust基于此要求程序媛给出具体的要求,指明要返回的那一部分的切片。
注意此方法要小心给出的切片大小,有可能会出现问题!

8.3 Storing Keys with Associated Values in Hash Maps

HashMap<K, V>k为keys的类型,V为值value的类型。通过一个hash函数来决定如何在内存中存放这些key和value。Hash map是的我们可以通过key来查看值

对于普通值例如i32,hash map依然采用copy的策略,原值不会失效;但对于String类型,原值会被move到Hash Map中,原值失效不能再使用。

附上第八章的代码笔记

#![allow(unused_variables)]
/**
    This is the code about Vector/String/Hash map
    My 3rd day as rustacean
    2020.02.17
**/

use std::collections::HashMap;

fn main() {
    ///vector
    let v: Vec <i32> = Vec::new();  //创建新的vector,由于新的vector为空,所以要显式声明其类型
    let v = vec![1, 2, 3, 4, 5];  //用此方法创建,rust可以自动识别类型

    let mut v= Vec::new();
    v.push(5);   //push method
    v.push(6);

    let v = vec![1, 2, 3, 4, 5];
    let third = &v[2];
    println!("The third element is {}", third);   //读取vector中元素的第一种方法:下标法这种方法当下标超过范围时,rust会panic

    match v.get(2){    //读取vector中元素的第二种方法:get() 当下标超过范围时,这种方式不会返回panic!!
        Some(third) => println!("The third element is {}", third),
        None => println!("There is no third element"),
    }

    let result = v.get(2);  //会输出 Some(3)!!!
    println!("{:?}",result);

    let v = vec![100, 32, 57];  //输出vector中全部的元素
    for i in &v {
        println!("{}", i);
    }

    //利用枚举实现一个vector中存储不同类型的元素
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }

    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];


    ///String

    let mut s = String::new();    //创建string
    let data = "initial contents";  //两种使用to_string的方法
    let s = data.to_string();

    let s = "another example".to_string();
    let s = String::from("initial contents");  //创建string

    let mut s = String::from("foo");  //updating string
    s.push_str("bar");  //push_str 不获取ownership


    let mut s = String::from("lo");
    s.push('l');  //push方法,可以添加单个char

    let s1 = String::from("Hello, ");
    let s2 = String::from("World!");
    let s3 = s1 + &s2;   //也可以使用加法进行连接,注意此处的使用,s1已经被move,不可再使用

    //连接多个string
    let s1 = String::from("tic");
    let s2 = String::from("tac");
    let s3 = String::from("toe");

    let s = format!("{}-{}-{}", s1, s2, s3);  //format宏指令,不会接管任何变量的ownership


    //注意indexing不适用于string

    for c in "नमस्ते".chars() {  //打印String 中的每一个元素,调用chars()方法
        println!("{}", c);
    }

    for b in "नमस्ते".bytes() {  //调用bytes()方法
        println!("{}", b);
    }

    ///Hash Maps
    let mut scores = HashMap::new();   //创建HashMap
    scores.insert(String::from("Blue"), 50);
    scores.insert(String::from("Yellow"), 30);


    let teams = vec![String::from("Blue"), String::from("Yellow")];
    let initial_scores = vec![10, 50];
    //本句中,HashMap<_, _>给rust指定了相应的输出结构。zip创建一个vector,其中元素为tuple,然后使用collect来将其转换为hash map
    let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();

    let team_name = String::from("Blue");  //获取Hash Map中的值,使用get方法
    let score = scores.get(&team_name);
    println!("The score of Blue is {:?}", score);    //输出结果为Some(10),因为get()方法会return 一个 Option<T>

    for (key, value) in &scores {   //另一个输出的方法
        println!("{}: {}", key, value);
    }

    //更新一个hash map
    //Overwriting a value
    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Blue"), 25);

    println!("{:?}", scores);   //重复插入相同key的不同值,则旧值会被抛弃

    //Only inserting a value if the key has no value
    scores.entry(String::from("Yellow")).or_insert(50);
    scores.entry(String::from("Blue")).or_insert(50);

    println!("{:?}", scores); //只插入不存在的值,之前存在的值不会发生改变

    //Updating a value Based on the Old Value
    let text = "Hello world wonderful world";
    let mut map = HashMap::new();
    for word in text.split_whitespace(){
        let count = map.entry(word).or_insert(0);
        *count += 1;
    }
    println!("{:?}", map);



}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值