Rust入门For前端开发

25 阅读6分钟

语法字典

【基础概念】rustnode/ts
扩展名.rs.js
执行命令rustc xx.rsnode xx.js
包管理Cargonpm
配置文件Cargo.tomlpackage.json
构建cargo buildnpm build
发布cargo build --releasenpm build:prod
构建并运行cargo runnpm build && open electron.app
检查代码cargo checktsc --noEmit or eslint
cargo newnpm init
Cargo.lockpackage.lock
其它crates.io/www.npmjs.com/
代码格式化RustfmtPrettier
【数据类型】rustts
无符号 32 位整数u32number
有符号 32 位整数i32number
浮点数f32number
boolboolean
char-
Stringstring
数组长度是固定let a = [1]let a = [1]
let a: [i32; 5]let a: number[]
let a = [0; 10]let a = new Array(10).fill(0)
无长度数组let a: Vec<i32> = Vec::new()let a: number[]
let a = vec![0; 10];let a = new Array(10).fill(0)
元组let tup: (i32, f64, u8) = (500, 6.4, 1)let tup = {0: 500, 1: 6.4, 2: 1}
  • 数组、元组 的长度/结构不可变

  • 数组 map 方法 : arr.map(() => 99) => arr.into_iter().map(\|_i\| 99).collect()

  • 数组 filter 方法 : arr2 = arr.filter((item) => item > 0) => arr2: Vec<&u8> = arr.iter().filter(\|i\| **i > 0).collect()

    • 需要注明 arr2 类型
    • iter()into_iter() 都用于遍历集合,前者不获取所有权,不会影响原集合,后者会消耗原数据,影响后续使用
【关键字和语法】rustts
fn a(x: i32) -> i32function a(x: number): number
函数返回值{ 5 } 多行也没有分号{ return 5; }
奇怪的特性\color{red} {奇怪的特性}letconst
let mutlet
constconst
format!("a+{b}=c")`a+${b}=c`
page.or(Some(1))page || 1
② 实例化String::new()new String()
关联函数\color{red} {关联函数}/静态方法ClassA::new()ClassA.new()
③ 引入包/库use std::ioimport fs from 'fs'
④ 引用/指针&item ref item-
.expect("msg")throw new Error('msg')
match n { X => .. other => somefn(other) }switch(n) { case X: .. default: fn(n) }
if n > 2if (n > 2)
三元操作let n = if condition { 5 } else { 9 }let n = condition ? 5 : 9
循环while n > 2while (n > 2)
(return 不是 break)for item in arrfor(let item of arr)
struct A { a: u32 }type A { a: npmber }
println! dbg!console.log

① 常量与只读变量:在 rust 中,变量可以被隐藏(Shadowing)

② 实例化:在 rust 中,实例化是借助类上的一个静态方法实现的,也就是说,方法名不一定非得是new

③ 引入包/库:有些命名空间的感觉,不会挂在某个变量上,可以直接调用其内部方法。(这样就无法从代码提示中看出一个包都有哪些功能,它也没有.d.ts,所以 rust 提供了cargo doc --open 来构建所有本地依赖提供的文档,并在浏览器中打开)

④ 引用/指针:详见下方,与传统指针不同。

⑤ match 既是语句也是表达式,if 也是如此。_可以做为缺省值,用它来替换 other 可以避免不使用它时导致的警告

Hello, world

// rust程序入口
fn main() {
    println!("Hello, world!");
}
$ rustc main.rs
# 在与 main.rs 同级处生成了可执行文件 main
# windows 上生成的是 exe 文件
$ ./main
Hello, world!

不同于 electron 的庞大和复杂,rust 使用简单几句代码就生成了一个可执行文件

脚手架

$ cargo new hello_cargo
$ cd hello_cargo
$ ls
Cargo.lock      Cargo.toml      src
$ ls src
main.rs
# 构建项目
$ cargo build
$ ./target/debug/hello_cargo
Hello, world!

GC /所有权/作用域/堆栈转移

rust 没有 GC,但它基于所有权可以做到有效释放内存:

  1. Rust 中的每一个值都有且只有一个 所有者(owner)。
  2. 当所有者(变量)离开作用域,这个值将被丢弃。
fn main () {
    let a: char = 'a' // a -> 栈(值)
    let str = String::from('a') // str -> 栈(指针)-> 堆(值)
}
  • 栈(Stack)后进先出,每个 item 大小占用已知且固定
  • 堆(Heap)无序,使用时需向内存分配器(memory allocator)申请指定大小的空间,并返回一个表示该位置地址的 指针(pointer)。指针是可以被放入栈中的

当函数被调用时,传递给函数的值(包括可能指向堆上数据的指针)和函数的局部变量被压入栈中。当函数结束时,这些值被移出栈。

如果将str这类变量赋值给其它变量时(比如做为参数传给其它方法),【其栈堆将被转移!

fn main () {
    let a: char = 'a' // a -> 栈(值)
    let str = String::from('a') // str -> 栈(指针)-> 堆(值)

    let b: char = a         // 复制了值
    let str3 = str.clone()  // 复制
    let str2 = str          // ❗️转移❗️了句柄,而不是复制
                            // 做为参数传出也是转移,意味着❗️后续不能再使用它

    change(str3)            // 转移,后续不可用
    change(&str3)           // 共享引用/只读引用,创建一个新指针指向str3的指针
    change(&mut str3)       // 可变引用。同一时间只能有一个可变引用❗️且不能同时存在可变、不可变引用❗️
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

引用允许你使用值但不获取其所有权。 引用

关于 不能同时存在可变、不可变引用

let r1 = &s; // 没问题
let r2 = &s; // 没问题
println!("{} and {}", r1, r2);
// 此位置之后 r1 和 r2 不再使用,此后声明的可变引用不算“同时” 诡异呀

let r3 = &mut s; // 没问题
println!("{}", r3);

一个简单的原则:大部分不带&的变量都会发生转移

解引用: *r3 = 99 将 99 赋值给 r3 指针指向的栈。因为 r3 中存储的是一个指针,需要变更是指针指向位置的值,而不是指针本身

struct 有实例方法的 TS type

语法上类似 TS 中的 type,但它们可以基于impl为自己添加实例方法

struct User {               // type user {
    active: bool,
    name: String,
    age: u64,
}
impl User {
    fn too_old(&self) -> bool {
        self.age > 35
    }
}
fn main() {
    let user1 = User {       // let user1:User = {
        active: true,
        name: String::from("someusername123"),
        age: 1,
    };
    dbg!(&user1, user1.too_old());
}

(所有实例)方法的第一个参数总是self

奇怪的枚举

enum IpAddrKind { V4, V6 }
let ipType = IpAddrKind::V4;

到目前为止还是正常的,接下来:

enum IpAddr {
    V4(String),
    V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));

// 甚至
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
impl Message {
    fn call(&self) {
        // ...
    }
}

V4V6 自带 set 方法。它还可以有实例方法 😱

这不就是上头的结构体吗?区别是实例只能是其定义中的一种变体,而不是一组数据。诡异!

枚举的使用

match home {
    IpAddr::V4 => println!("这是个 ipv4"),
    IpAddr::V6 => println!("这是个 ipv6"),
}
let ip_ver: i32 = match home {
    IpAddr::V4 => 4,
    IpAddr::V6 => 6,
};

let flag = 9;
match flag {
    0 => success(),
    1 => gotoLogin(),
    2 => gotoBlacklist(),
    other => handleError(other),
}

内置枚举 Option 与 Some、None

rust 以其完全性闻名。它是没其它语言中的 null 概念的,但它引入了一个 Option 枚举,用来处理需要 null 概念的情况

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

// 在 rust 中使用 null 值
let mut age: Option<i32> = None;  // Option<i32> => number | null
let usefulAge = age.or(Some(18)); // ts => age || 1

fn getAge() -> Option<i32> {
    if SOME_LOGIC {
        Some(9)                   // Option下的有值枚举
    } else {
        None
    }
}
age = getAge();
match age {
    Some(value) => println!("age is {}", value),  // 有值枚举
    None => println!("age is empty"),             // 无值枚举
}

if some_value.is_none() {
    println!("它是无值枚举,即 None");
}
if some_value.is_some() {
    let value = some_value.unwrap(); // 获取Option中的值,调用后some_value不再可用
    println!("它是有值枚举,值为: {}", value);
}
if some_value == Some(53) {
    println!("== 53");
} else {
    println!("!= 53");
}

内置枚举 Result

enum Result<T, E> {
    Ok(T),
    Err(E),
}
fn main() {
    let result = get_age();
    match result {
        Ok(value) => println!("年龄是 {}", value),
        Err(value) => println!("Error: {}", value),
    }
}
fn get_age() -> Result<u32, String> {
    let age = 16;
    if age < 18 {
        Err(String::from("年龄不合法"))
    } else {
        Ok(age)
    }
}

tinyzzh.github.io/rust/2023/0…

vector 是大小可变的数组

// 数组
let arr = [3, 4, 5];
println!("不会引发错误的获取 {}", arr.get(100)); // Option<&i32>
for ele in arr {
    println!("arr for > {ele}");
}
for ele in arr.iter() {
    println!("arr iter for > {ele}");
}
for (index, ele) in arr.iter().enumerate() {
    println!("arr iter with index for > {index}-{ele}");
}
// 不会引发错误的获取 None
// arr for > 3
// arr for > 4
// arr for > 5
// arr iter for > 3
// arr iter for > 4
// arr iter for > 5
// arr iter with index for > 0-3
// arr iter with index for > 1-4
// arr iter with index for > 2-5
// vector 所有值在内存中彼此相邻排列
let vvv = vec![3, 4, 5];
let first = &v[0];
vvv.push(6);    // error:官方给的解释是:在没有足够空间将所有所有元素依次相邻存放的情况下,可能会要求分配新内存并将老的元素拷贝到新的空间中。这时,第一个元素的引用就指向了被释放的内存。但改为 vvv[2] = 99 也同样不行

for ele in vvv {
    println!("vvv for > {ele}");
}
for ele in vvv.iter() {
    println!("vvv iter for > {ele}");
}
for (index, ele) in vvv.iter().enumerate() {
    println!("vvv iter with index for > {index}-{ele}");
}
// 以上代码会报错!因为在调用`for ele in vvv`之后 vvv 就不再可用了!
error[E0382]: borrow of moved value: `vvv`
|     for ele in vvv {
|                --- `vvv` moved due to this implicit call to `.into_iter()`
...
|     for ele in vvv.iter() {
|                ^^^^^^^^^^ value borrowed here after move

kaisery.github.io/trpl-zh-cn/…

rustwiki.org/zh-CN/rust-…

HashMap

需要手动引入use std::collections::HashMap;

rust HashMapts Map
let mut map: HashMap<String, i32> = HashMap::new()let map = new Map<string, number>()
map.insert(String::from("Blue"), 10)map.set('Blue', 30)
map.get("Blue")map.get('Blue')
map.get("Blue").copied().unwrap_or(0)map.get('Blue') || 0
map.entry(String::from("Blue")).or_insert(50)!map.has('Blue') && map.set('Blue', 50)
for (key, value) in &map { println!("{key}: {value}"); }map.forEach((val, key) => console.log(val, key))

or_insert会返回一个&mut对象,可能直接修改其内部的值

let mut map = HashMap::new();

let text = "hello world wonderful world";
for word in text.split_whitespace() {
    let count: &mut i32 = map.entry(word).or_insert(0);
    *count += 1;
}

println!("{map:?}");

practice-zh.course.rs/basic-types…