Rust入门笔记 1.概述、变量与基本类型

yyi
yyi
2023-08-04 / 0 评论 / 145 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2023年08月09日,已超过447天没有更新,若内容或图片失效,请留言反馈。

Rust入门笔记 —— 概述,变量与基本类型

在我学习的过程中,一直坚持着实践掌握的更快的方法论。本笔记是我使用Rust By Examplepractice.rs学习Rust过程的记录,可能更多偏向于后者,因为他是中文的... 。我打算使用的路线是通过这两个Wiki快速掌握Rust的语法,然后阅读一些优秀的项目来让自己的代码变的优雅。最后开发几个中型项目来巩固,可能会包括Redis(我一直想通过开发的方式让我深入了解Redis)和一个小内核(如果时间允许),或者一个小编译器(如果时间不允许)。

我的最终目的是可以让Rust成为我在学习计算机系统结构中的主要开发语言。

我使用 Vscode + Docker作为我的学习环境,我对C/C++有比较高的熟练度,Python和Go也是我的主力语言。对其他语言也有一定了解,能勉强使用(如Swift、Java和Js)。我不认为Rust适合作为第一门语言学习。希望这能让你了解到我的大概水平,以便更好的让这篇笔记帮助到你。

0 环境配置

我是Windows下的,直接用Docker了

docker run -it --name rust-dev --privileged=true \
-p 2222:22 -v /f/Dev_rs:/rs/work rust bash

注意-v是挂载的目录,要符合你的实际情况

-p是把docker的22端口映射到本机的2222端口上,方便使用CLion的ssh,入门期间没有太大的项目,以Vscode为主。

1 变量、变量绑定

Rust是一类静态语言,变量需要在初始化后使用

通常,我们使用 let var_name[: var_type] = [value]; 的格式对变量进行声明。

let有类似auto的功效,如果有value,可以不声明type,rust会自动进行类型推断。对于这种声明,我们称为把value绑定到变量

1.1 变量与常量

rust 包括变量和常量,其中变量还会分为可变变量和不可变变量,最显著的区别如下:

let a = 123; // 被自动推断为i32, 如果需要,显式声明为i64

a = 456; // Error : cannot assign twice to **immutable** variable `a`
let a = 456; // Right : 重新绑定了
// ----------------
const a: i32 = 123; // const 需要声明类型
let a = 456; // missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable

1.2 可变变量

注意到第三行报错中的immutable了么,我们需要显式的为一个变量声明为可变,才能在不重新绑定的前提下对其进行修改、运算等操作

let mut a = 1;
a += 2;
println!("a = {}", a);

// a = 3

这里出现了一个 'println!',我们暂时不探究宏是什么以及他是怎么工作的,我们只需要知道它可以格式化的输出内容,形式上类似于Python的格式化字符串,即使你不会,你也能猜到它都输出了些什么,在上面的例子中,编译器把第二个参数a填入到了格式串的{}中,所以输出了 "a = 3" 的结果。

1.3 未使用、未生明、作用域

对于未使用的变量,rust编译器会报错。我们可以在变量前加'_',相当于显式的声明他是一个可能不会被用到的变量。

fn main() {
    let a = 10;
}
// warning: unused variable: `a`

rust编译器禁止使用未初始化的变量,会在编译期报错。

对于变量作用域,和其他语言差不多,不做赘述。rust不允许全局的普通变量,必须是const或者static量

1.4 变量遮蔽(Shadowing)

变量遮蔽我理解类似于重载,在C中也很常见,只不过我们一般不这么用。后声明的变量只会在其作用域中有效,出了自己的作用域后就归还到原来的变量。

fn main() {
    let x: i32 = 5;
    {
          assert_eq!(x, 5);
        let x = 12;
        assert_eq!(x, 12);
    }

    assert_eq!(x, 5);

    let x = 42;
    println!("{}", x); // 输出 "42".
}

1.5 变量解构

fn main() {
    let (mut x, y) = (1, 2);
    x += 2;

    assert_eq!(x, 3);
    assert_eq!(y, 2);
}

对于Python,我们经常使用对等的独立变量去接一个Tuple的拆分(典型的是函数返回值),Rust也可以这样

在Rust 1.59+中,解构式还可以进行赋值。更多在后面 tuple 提到

fn main() {
    let (mut x, mut y);
    (x, y) = (3, 4);
    [x, y] = [1, 2];
    // 填空,让代码工作
    assert_eq!([x,y], [1, 2]);
} 

2 基本类型

2.1 数值类型

对于整形,包括(8、16、32、64、128) x (i/u) 的笛卡尔积种,字面意思很好理解

这个让我们填as后面的,了解到通过as进行字面量的强转
fn main() {
    let v: u16 = 38_u8 as u16; 
}
可以看到默认的字面整形量推断出来的变量类型是i32
// 修改 `assert_eq!` 让代码工作
fn main() {
    let x = 5;
    assert_eq!("i32".to_string(), type_of(&x));
}

// 以下函数可以获取传入参数的类型,并返回类型的字符串形式,例如  "i8", "u8", "i32", "u32"
fn type_of<T>(_: &T) -> String {
    format!("{}", std::any::type_name::<T>())
} 
// 填空,让代码工作
fn main() {
    assert_eq!(i8::MAX, 127); 
    assert_eq!(u8::MAX, 255); 
}

对于字面量,用 [0-9_] 表示十进制数,0x[0-9A-F]表示十六进制数、0o、0b表示八进制、二进制数。

对于浮点数,f32、f64分别代表32和64位浮点数,字面量默认为64位浮点数

对于布尔,bool只能为true或者false, 不能用0代表false、非0代表true

对于字符,用char表示,char大小为4个字节,是Unicode标量

这个例子让我们改assert的char大小,因为是四字节,直接改为4
// 修改2处 `assert_eq!` 让代码工作
use std::mem::size_of_val;
fn main() {
    let c1 = 'a';
    assert_eq!(size_of_val(&c1),4);  // original : 1
    let c2 = '中';
    assert_eq!(size_of_val(&c2),4);  // original : 3
    println!("Success!")
} 

2.2 tuple & list

用 () 表示 tuple 元组,典型的例子如下:

let tup: (i32, f64, u8) = (500, 6.4, 1);
// tup.0 等于 500
// tup.1 等于 6.4
// tup.2 等于 1
let (x, y, z) = tup;
// y 等于 6.4
imp函数返回的是一个空tuple (虽然暂时我还不知道为什么是这样),但是为了让它们相等,我们给v一个空tuple即可
// 让代码工作,但不要修改 `implicitly_ret_unit` !
fn main() {
    let _v: () = ();
    let v = (); // Orig : (2, 3)
    assert_eq!(v, implicitly_ret_unit());
    println!("Success!")
}
fn implicitly_ret_unit() {
    println!("I will return a ()")
}
// 不要使用下面的函数,它只用于演示!
fn explicitly_ret_unit() -> () {
    println!("I will return a ()")
}
这个例子说明空tuple的占用空间为0,而非cpp中空类的1
// 让代码工作:修改 `assert!` 中的 `4` 
use std::mem::size_of_val;
fn main() {
    let unit: () = ();
    assert!(size_of_val(&unit) == 0);

    println!("Success!")
}

当然,tuple 也可以复合

let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);

tuple可以直接通过下表解构、可以直接被 println 打印

println!("{:?}", tuple_of_tuples.0);
// (1, 2, 2)

和 python 一样,需要一个 ', '区别括起来的数和 tuple

println!("{:?} {:?}", (1), (1, ));
// 1 (1,)

用 [] 表示数组,包括同类型数据

let a = [1, 2, 3, 4, 5];
// a 是一个长度为 5 的整型数组
let b = ["January", "February", "March"];
// b 是一个长度为 3 的字符串数组
let c: [i32; 5] = [1, 2, 3, 4, 5];
// c 是一个长度为 5 的 i32 数组
let d = [3; 5];
// 等同于 let d = [3, 3, 3, 3, 3];
let first = a[0];
let second = a[1];
// 数组访问
let first = a.get(0);
// 会返回一个 Option,提供给我们 match 用,后面再说

2.3 Range

range的表达方式很简单,就是 a..b表示a到b左闭右开, 比如 -3..2表示 [-3, 2),a..=b表示闭区间

两个目标: 1. 修改 assert! 让它工作 2. 让 println! 输出: 97 - 122
fn main() {
    let mut sum = 0;
    for i in -3..2 { 
        sum += i
    }

    assert!(sum == -5);

    for c in 'a'..='z' {
        println!("{}",c as u8); // original : no as u8
    }
}
// 填空
use std::ops::{Range, RangeInclusive};
fn main() {
    assert_eq!((1..5), Range{ start: 1, end: 5 });
    assert_eq!((1..=5), RangeInclusive::new(1, 5));
}
0

评论 (0)

取消