[1]Rust基础语法

268 阅读9分钟

安装 Rust 编译器

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

img.png

创建一个工程

cargo new --bin helloworld

使用 cargo build 来编译。

使用 cargo run 命令可以直接运行程序。

Rust基础类型

赋值语句

使用let关键字定义变量以及初始化

fn  main() {
    let x: u32 = 5;
    println!("The value of x is: {}", x);
}

Rust中的类型写在变量名的后面,使用冒号分隔。

数字类型

整数

img_1.pngimg_1.png

isize 和 usize 的位数与具体 CPU 架构位数有关。CPU 是 64 位的,它们就是 64 位 的,CPU 是 32 位的,它们就是 32 位的。这些整数类型可以在写字面量的时候作为后缀 跟在 后面,来直接指定值的类型,比如 let a = 10u32; 就定义了一个变量 a,初始化成无符号 32 位整型,值为 10。

整数字面量的辅助写法

  • 十进制字面量 98_222,使用下划线按三位数字一组隔开
  • 十六进制字面量 0xff,使用0x开头
  • 8进制字面量 0o77,使用0o(小写字母o)开头
  • 二进制字面量 0b1111_0000,使用0b开头,按4位数字一组隔开
  • 字符的字节表示 b'A',对一个ASCII字符,在其前面加b前缀,直接得到此字符的ASCII码值

浮点数

浮点数有两种类型:f32 和 f64,分别代表 32 位浮点数类型和 64 位浮点数类型。它们也可 以跟在字面量的后面,用来指定浮点数值的类型,比如 let a = 10.0f32; 就定义了一个 变量 a,初始化成 32 位浮点数类型,值为 10.0。

布尔类型

布尔类型有两个值:true 和 false。Rust 中的布尔类型使用 bool 表示。

fn main() {
    let t = true;
    let f: bool = false; // 显式指定类型注解
    println!("t = {}, f = {}", t, f);
}
​

字符类型

Rust 中的字符类型是 char,值用单引号括起来。char 类型的值代表一个 Unicode 标量值,在内存中占用4个字节。

fn main() {
    let c = 'z';
    let z = 'ℤ';
    let heart_eyed_cat = '😻';
    println!("c = {}, z = {}, heart_eyed_cat = {}", c, z, heart_eyed_cat);
}

字符串

Rust 有两种字符串类型:String 和 &str,它们都是 UTF-8 编码的字节序列。String 类型不能通过下标索引,因为 Rust 的 String 类型不是原生类型,而是一个 Vec 的封装。&str 类型是一个引用,它的值是一个字符串字面量的切片,类型签名是 &str,它通常以不可变引用的形式出现,即 &str。

let s = "hello";
let mut s = String::from("hello");

字符串字面量中的转义

Rust 中转义符号也是反斜杠 \ ,可用来转义各种字符。

fn main() {
    let s = "hello\nworld";
    println!("{}", s);
}

Rust 还支持通过 \x 输入等值的 ASCII 字符,以及通过 \u{} 输入等值的 Unicode 字符。

fn main() {
    let s = "\x52\x75\x73\x74\x20\xe6\x97\xa0\xe7\xba\xbf\x20\xe7\xbc\x96\xe7\xa8\x8b";
    println!("{}", s);
}

禁止转义

Rust 中的字符串字面量默认是转义的,如果想要禁止转义,使 用 r"" 或 r#""# 把字符串字面量套起来就行了。如果遇到字面量里面有#号的情况,可以在r后面,加任意多的前后配对的#号,比如 r###"hello"###,这样就可以避免转义了。

fn main() {
    let s = r"\x52\x75\x73\x74\x20\xe6\x97\xa0\xe7\xba\xbf\x20\xe7\xbc\x96\xe7\xa8\x8b";
    println!("{}", s);
}

字节串

用 b 开头,双引号括起来,比如 b"this is a byte string"。这时候字符串的类型已不是字符串,而是字节的数组 [u8; N],N 为字节数。

fn main() {
    let s = b"hello";
    println!("{:?}", s);
}
​
fn main() {
    // 字节串的类型是字节的数组,而不是字符串了
    let bytestring: &[u8; 21] = b"this is a byte string";
    println!("A byte string: {:?}", bytestring);
    // 可以看看下面这串打印出什么
    let escaped = b"\x52\x75\x73\x74 as bytes";
    println!("Some escaped bytes: {:?}" , escaped);
    // 字节串与原始字面量结合使用
    let raw_bytestring = br"\u{211D} is not escaped here";
    println!("{:?}", raw_bytestring);
}

字节串很有用,特别是在做系统级编程或网络协议开发的时候,经常会用到。

数组

Rust 中的数组是 array 类型,用于存储同一类型的多个值。数组表示成[T; N],由中括号括起来,中间用分号隔开,分号前面表示类型,分号后面表示数组长度。

fn main() {
    let a: [i32; 5] = [1, 2, 3]; // a: [i32; 3]
    let mut m = [1, 2, 3]; // m: [i32; 3]
    let a = [0; 20]; // a: [i32; 20]
    println!("{:?}", a);
}

动态数组

Rust 中的动态数组类型是 Vec(Vector),也就是向量,中文翻译成动态数组。它用来存储 同一类型的多个值,容量可在程序运行的过程中 动态地扩大或缩小,因此叫做动态数组。

fn main() {
    let v = vec![1, 2, 3, 4, 5]; // v: Vec<i32>
    let v = vec![0; 10]; // v: Vec<i32>
    
    let mut v = Vec::new(); // v: Vec<i32>
    v.push(1);
    v.push(2);
    v.push(3);
    println!("{:?}", v);
}

哈希表

Rust 中的哈希表类型为 HashMap。对一个 HashMap 结构来说,Key 要求是同一种类型,比如是字符串就统一用字符串,是数字就统一用数字。Value也是一样,要求是同一种类型。Key 和 Value 的类型不需要相同。

use std::collections::HashMap;
fn main() {
    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
    println!("{:?}", scores);
}

Rust复合类型

元组

元组是一个固定(元素)长度的列表,每个元素类型可以不一样。用小括号括起来,元素之间用逗号隔开。

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let (x, y, z) = tup;
    println!("The value of y is: {}", y);
    println!("The value of tup.0 is: {}", tup.0);
}

与数组的相同点是,它们都是固定元素个数的,在运行时不可伸缩。与数组的不同点是,元组 的每个元素的类型可以不一样。元组在 Rust 中很有用,因为它可以用于函数的返回值,相当 于把多个想返回的值捆绑在一起,一次性返回。

当没有任何元素的时候,元组退化成 (),就叫做 unit 类型,是 Rust 中一个非常重要的基础类 型和值,unit 类型唯一的值实例就是 (),与其类型本身的表示相同。比如一个函数没有返回值 的时候,它实际默认返回的是这个 unit 值。

结构体

结构体它由若干字段组成,每个字段的类型可以不一样。Rust 中使用struct 关键字来定义结构体。

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
fn main() {
    let user1 = User {
        email: String::from("333"),
        username: String::from("222"),
        active: true,
        sign_in_count: 1,   
    };  
    println!("{}", user1.email);
}

枚举

Rust 中使用 enum 关键字定义枚举类型。枚举类型的值只能是已知的几个值中的一个,比如下面的代码定义了一个枚举类型 IpAddrKind,它的值只能是 V4 或 V6。

enum IpAddrKind {
    V4,
    V6,
}
fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;
    println!("{:?}", four);
}

枚举类型里面的选项叫做此枚举的变体(variants)。变体是其所属枚举类型的一部分。在上面的代码中,IpAddrKind::V4 和 IpAddrKind::V6 就是变体。 枚举类型的变体可以有关联的数据,比如下面的代码定义了一个枚举类型 Message,它有两个变体 Quit 和 Move,其中 Move 变体有两个关联的数据,分别是 x 和 y。

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
fn main() {
    let m = Message::Move { x: 1, y: 2 };
    match m {
        Message::Quit => {
            println!("The Quit variant has no data to destructure.")
        }
        Message::Move { x, y } => {
            println!(
                "Move in the x direction {} and in the y direction {}",
                x, y
            );
        }
        Message::Write(text) => println!("Text message: {}", text),
        Message::ChangeColor(r, g, b) => println!(
            "Change the color to red {}, green {}, and blue {}",
            r, g, b
        ),
    }
}

# 控制流

分支语句

Rust 中使用 if else 来构造分支。

fn main() {
    let number = 3;
    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false");
    }
}

循环语句

Rust 中有三种循环语句,分别是 loop、while、for。 loop 是一个无限循环,它不会结束,除非遇到 break 语句,或者程序 panic 了。loop 语句的返回值是 (),也就是 unit 类型的值。

fn main() {
    let mut counter = 0;
    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2;
        }
    };
    println!("The result is {}", result);
}

while 循环是一个条件循环,它在每次循环之前判断条件是否成立,如果成立则继续循环,否则结束循环。while 循环的返回值是 (),也就是 unit 类型的值。

fn main() {
    let mut number = 3;
    while number != 0 {
        println!("{}!", number);
        number -= 1;
    }
    println!("LIFTOFF!!!");
}

for 循环是一个遍历循环,它可以遍历一个迭代器(iterator)中的每一个元素。for 循环的返回值是 (),也就是 unit 类型的值。

fn main() {
    let a = [10, 20, 30, 40, 50];
    for element in a {
        println!("the value is: {}", element);
    }
}

对于循环的场景,Rust 还提供了一个便捷的语法来生成遍历区间: ..(两个点)。

fn main() {
    for number in (1..4).rev() {
        println!("{}!", number);
    }
    println!("LIFTOFF!!!");
​
    for ch in 'a'..='z' {
        println!("{ch}");
    }
}

函数和模块

函数

在Rust 中使用 fn 关键字来定义一个函数。

fn main() {
    println!("Hello, world!");
    another_function();
}
fn another_function() {
    println!("Another function.");
}

函数定义时的参数叫作形式参数(形参),函数调用时传入的参数值叫做实际参数(实参)。 函数的调用要与函数的签名(函数名、参数个数、参数类型、参数顺序、返回类型)一致,也就是实参和形参要匹配。

闭包

闭包是另一种风格的函数。它使用两个竖线符号 || 定义,而不是用 fn () 来定义。你可以 看下面的形式对比。

fn main() {
    let plus_one = |x: i32| x + 1;
    let a = 6;
    println!("{} plus one is {}", a, plus_one(a));
}

模块

Rust 中使用 mod 关键字来定义一个模块,模块里面可以包含函数、结构体、枚举、常量、 trait 等等。模块里面的项默认是私有的,可以使用 pub 关键字来修饰,使得它们变成公有的。

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
fn main() {
    front_of_house::hosting::add_to_waitlist();
}