Rust基础

邂逅 Rust

学习Rust基础的第一天

安装Rust

Rust安装起来很方便,你可以使用 https://rustup.rs/ 给出的方法,根据你的操作系统进行安装。

vscode插件

  1. rust-analyzer:它会实时编译和分析你的 Rust 代码,提示代码中的错误,并对类型进行标注。你也可以使用官方的 Rust 插件取代
  2. Rust syntax:为代码提供高亮
  3. Dependi: 帮助你分析当前项目的依赖是否是最新的版本
  4. Even Better TOML:Rust使用toml做项目的配置管理。better toml可以帮你语法高亮,并展示toml中的错误
  5. Rust test lens:可以帮你快速运行某个Rust测试
  6. Tabnine:基于AI的自动补全,可以帮助你更快地撰写代码。

案例练习

通过使用htpp请求Rust官网首页,然后把获得的HTML转成Markdown保存

首先使用cargo new scrape_url生成一个新的项目,默认情况下,这条命令会生成一个可执行项目scrape_url,入口文件在src/main.rs。我们在Cargo.toml里,加如下依赖

[dependencies]
reqwest = { version = "0.12", features = ["blocking"] }
html2md = "0.2"

Cargo.toml是Rust项目的配置管理文件,它符合toml语法。

我们这个项目添加了两个依赖:

  1. reqwest: htpp请求客户端
  2. html2md:把HTML文本转成Markdown

接下来,在src/main.rs里,我们为main()函数加入以下代码:

use std::fs;

fn main() {
    let url = "https://www.rust-lang.org";
    let output = "rust.md";

    println!("发起网络请求:{}", url);
    let body = reqwest::blocking::get(url).unwrap().text().unwrap();
    println!("正在从hmtl转到markdown中...");
    let md = html2md::parse_html(&body);

    fs::write(output, md.as_bytes()).unwrap();
    println!("转换完成,保存名称为:{} 保存在当前目录文件夹下", output);
}

运行成功后,可以在项目目录文件夹下看到rust.md,里面的内容就是从https://www.rust-lang.org获取到的html文本转换成markdown格式的结果。

案例总结

在这个项目中我们可以直观地感受到Rust的一些基本特点:

首先,Rust使用名为cargo的工具来管理项目,他类似Nodejs的npm用来做依赖管理以及开发过程中的任务管理,比如编译、运行、测试、代码格式化等等。

另外,你也容易看到,Rust虽然是一门强类型语言,但编译器支持类型推导,这使得写代码时的直观感受和写脚本差不多

这里例子没有展现出来,但Rust还具备的其他特点有:

Rust的变量默认是不可变的,如果要修变量值,需要显示地使用mut关键字。

除了let/static/con/fn等少数语句外,Rust绝大多数代码都是表达式。所以if/while/for/loop都会返回一个值,函数最后一个表达式就是函数的返回值,这和函数式编程语言一致。

基础语法和基础数据类型

首先我们看在Rust下,我们如何定Rust变量、函数和数据结构

变量和函数

前面说到,Rust支持类型推导,在编译器能够推导类型的情况下,变量类型一般可以省略,但常量(const),和静态变量(static)必须声明类型。

定义变量的时候,根据需要,你可以添加mut关键字让变量变得具备可变性。默认变量不可变是一个重要的特性,有助于我们写出健壮且正确的代码。当你使用mut却没有修改变量,Rust编译器会友好地报警,提示你移除不必要的mut

在Rust下,函数是一等公民,可以作为参数或者返回值,我们来看一个函数作为参数的例子:

fn apply(value: i32, f: fn(i32) -> i32) -> i32 {
    f(value)
}

fn square(value: i32) -> i32 {
    value * value
}

fn cube(value: i32) -> i32 {
    value * value * value
}

fn main() {
    println!("apply square: {}", apply(2, square));
    println!("apply cube: {}", apply(2, cube))
}

运行结果:

apply square: 4
apply cube: 8

代码中的fn(i32) -> 32是apply函数第二个参数的类型,它表明接受一个函数作为参数,这个传入的函数必须是:参数只有一个。且类型为i32,返回值类型也是i32

Rust函数参数的类型和返回值类型都必须显示定义,如果没有返回值可以忽略,返回unit。函数内部如果提前返回,需要用return关键字,否则最后一个表达式就是其返回值。如果最后一个表达式后添加了;分号,隐含其返回值为unit。可以看一下例子:

fn pi() -> f64 {
    3.1415926
}

fn not_pi() {
    3.1415926;
}

fn main() {
    let is_pi = pi();
    let is_unit1 = not_pi();
    let is_unit2 = {
        pi();
    };

    println!("is_pi: {:?}, is_unit1: {:?}, is_unit2: {:?}", is_pi, is_unit1, is_unit2);
}

运行结果:

is_pi: 3.1415926, is_unit1: (), is_unit2: ()