概述

Rust 是一门快速、高并发、安全且具有授权性的语言,由 Mozilla 开发。

作为一门静态强类型语言,它拥有静态类型检查和禁止自动类型转换的特性,可以安全地重构代码,在编译时捕获大多数错误。作为一门多范式的语言,不仅可以编写操作系统、游戏引擎和许多性能关键型应用程序,还可以编写高性能 Web 程序、网络服务、类型安全的 ORM 库,还可以将程序编译为 WebAssembly 在浏览器中运行,或者为嵌入式平台构建安全性优先的实时应用程序。

安装

Rust 工具链由编译器 rustc 和软件包管理器 Cargo 组成,使用 rustup 来安装它们:

curl https://sh.rustup.rs -sSf | sh

默认情况下会安装稳定版的 rustc 和 Cargo 到 ~/.cargo 目录下,并更新 PATH 变量。

要将工具链更新到最新版本:

rustup update

要将 rustup 更新到最新版本:

rustup self update

程序结构

Rust 程序会被组织成模块的形式,可执行程序的根模块通常是 main.rs,库文件的根模块则是 lib.rs。

use std::env;

fn main() {

let name = env::args().skip(1).next();

match name {

Some(n) => println!("Hi there! {}", n),

None => panic!("Didn't receive any name?")

}

}

use从std库中导入了一个模块env。args是env模块的的一个函数,返回传递给程序的参数的迭代器。由于第 1 个参数是函数名,所以用skip()跳过它。由于迭代器是惰性的,并且不会预先计算,所以调用next(),返回一个名为Option的枚举类型。Option的值可能是Some也可能是None,因为用户可能没有提供参数。match表达式将检查Option的值,并执行对应的分支。

基本数据类型

bool类型用true表示真,false表示假。char表示单个字符。整数,根据位宽和是否有符号分为i8、i16、i32、i64、i128、u8、u16、u32、u64、u128、isize和usize。浮点数根据位宽分为f32和f64。[T; N]表示固定数组的大小,T表示元素的数据类型,N表示元素数量,并且是编译器非负常数。[T]表示动态大小的连续序列的视图,T表示元素的数据类型。str表示字符串切片,主要用作引用,即&str。(T1, T2, ...)表示有限序列,TN可以是不同的数据类型。fn(T1)->T2表示接收参数T1并返回T2类型的函数,如果有多个参数,用,隔开。

声明变量

用let声明一个变量,但这个变量是不可变的,初始化后便不能再为其赋值。变量名后面的:用于标注该变量的数据类型,如果省略它的话,编译器会尝试根据初始化的值为这个变量推断一个合适的数据类型。

let a = 42;

let pi: f32 = 3.14;

用let mut声明一个可变变量,可以用=为这个变量再次赋值。

let mut a = 42;

a = 13;

函数

用fn定义一个函数,()中的是函数可以接收的参数,->后面的是函数的返回值类型。

fn add(a: i64, b: i64) {

a + b

}

调用函数:

let a = 17;

let b = 3;

let result = add(a, b); // result: 20

如果希望修改函数的参数,可以在参数名前面加上mut:

fn increase_by(mut val: u32, how_much: u32) {

val += how_much;

println!("You made {} points", val);

}

函数体最后一行的表达式就是函数的返回值(注意没有以;结尾),也可以用return关键字提前返回。

闭包

Rust 的闭包本质上是一个匿名函数,它可以捕获声明它的环境中的变量。闭包的主体可以是单一表达式或{}中的语句块。

let doubler = |x| x * 2;

let value = 5;

let twice = doubler(value);

println!("{} doubled is {}", value, twice);

let big_closure = |b, c| {

let z = b + c;

z * twice

};

let some_number = big_closure(1, 2);

println!("Result from closure: {}", some_number);

闭包主要用作高阶函数的参数,高阶函数是一个以另一个函数或闭包作为参数的函数。

字符串

字符串在 Rust 中有两种形式:&str类型或String类型。String类型是分配在堆上的,&str则是指向现有字符串的指针,这些字符串既可以在堆上也可以在栈上,也可以是已编译对象代码的数据段中的字符串。

let question = "How are you?";

let person: String = "Bob".to_string();

let namaste = String::from("春日一番");

println!("{}! {} {}", namaste, question, person);

上述代码的question是&Str类型,person和namaste是String类型的。Rust 的字符串均为 UTF-8 编码。

&是一个运算符,用于创建指向任何类型的指针。

条件和判断

if

Rust 的if是一个表达式而非语句,意味着它总是会返回一个值(既可以是empty类型的(),也可以是实际的值),即花括号最后一行的表达式,因此if和else应该具有相同类型的返回值。

let result = if 1 == 2 {

"Wait, what?"

} else {

"Rust make sense"

};

println!("You know what? {}.", result);

如果if有一个返回值,则else分支是不可省略的,否则当条件为false时结果将是(),编译器会报错。要省略else分支,则if分支不能有返回值,可以在最后一行的表达式后面加上;告诉编译器丢弃这个值。

match

match会根据给定的值来决定执行哪个分支,相较if适合处理更加复杂的多值多条件判断。

fn req_status() -> u32 {

200

}

fn main() {

let status = req_status();

match status {

200 => println!("Success"),

404 => println!("Not Found"),

other => {

println!("Request failed with code: {}", other);

}

}

}

match在{}中的分支被称为匹配臂,每个匹配臂必须返回相同类型的值。=>左侧是待选的值,右侧是表达式,如果是单行表达式还需要用,分隔,other表示上面给出的待选值均不匹配的情况。

和if一样,match表达式也可以将返回值赋给一个变量。

循环

loop

loop表示无限循环,用break语句可以跳出循环,如果使用名称标记循坏代码块还可以用break直接跳出指定的循环体。

fn silly_sub(a: i32, b: i32) -> i32 {

let mut result = 0;

'increment: loop {

if result == a {

let mut dec = b;

loop {

if dec == 0 {

break 'increment;

} else {

result -= 1;

dec -= 1;

}

}

} else {

result += 1;

}

}

result

}

fn main() {

let a = 10;

let b = 4;

let result = silly_sub(a, b);

println!("{} minus {} is {}", a, b, result);

}

while

while会在条件成立时一直循环。

let mut x = 100;

while x > 0 {

println!("{} more runs to go", x);

x -= 1;

}

for

for只适用于可以转换为迭代器的类型。

for i in 0..10 {

print!("{},", i);

}

0..10表示从0到9的整数序列,0..=10表示从0到10的整数序列。

自定义数据类型

struct

struct用于声明结构体,结构体的声明形式有 3 种:单元结构体、元组结构体和类 C 语言的结构体。

单元结构体只需要给出名称即可,运行时不占用任何空间,也不关联任何数据,通常用于表述错误或状态。

struct Dummy;

fn main() {

let value = Dummy;

}

元组结构体的成员没有命名,而是根据在定义中的位置来访问。Rust 中通过索引访问成员使用.而不是[]。

struct Color(u8, u8, u8);

fn main() {

let white = Color(255, 255, 255);

let red = white.0;

let green = white.1;

let blue = white.2;

println!("White: R:{}, G:{}, B:{}", red, green, blue);

}

还可以将结构体变量直接解构赋值给多个变量。如果希望丢弃某个值,可以用_替代对应的变量。

let orange = Color(255, 165, 0);

let Color(red, green, blue) = orange;

println!("Orange: R:{}, G:{}, B:{}", red, green, blue);

类 C 语言的结构体在声明时需要给出成员的数据类型,成员之间用,隔开,{}后面不需要加;。

struct Player {

name: String,

iq: u8,

friends: u8,

score: u16

}

fn bump_player_score(mut player: Player, score: u16) {

player.score += score;

println!("Updated player stats:");

println!("Name: {}", player.name);

println!("IQ: {}", player.iq);

println!("Friends: {}", player.friends);

println!("Score: {}", player.score);

}

fn main() {

let name = "Alice".to_string();

let player = Player {

name,

iq: 171,

friends: 134,

score: 1129 };

bump_player_score(player, 120);

}

在结构体前面加上mut表示该结构体的所有成员都是可修改的。

enum

enum用于声明枚举。

#[derive(Debug)]

enum Direction {

N,

E,

S,

W

}

enum PlayerAction {

Move {

direction: Direction,

speed: u8

},

Wait,

Attack(Direction)

}

fn main() {

let simulated_player_action = PlayerAction::Move {

direction: Direction::N,

speed: 2,

};

match simulated_player_action {

PlayerAction::Wait => println!("Player wants to wait"),

PlayerAction::Move { direction, speed } => {

println!("Player wants to move in direction {:?} with speed {}", direction, speed)

}

PlayerAction::Attack(direction) => {

println!("Player wants to attack direction {:?}", direction)

}

};

}

类型上的函数和方法

impl块可以为自定义的数据类型或包装器类型提供行为的实现。

struct Player {

name: String,

iq: u8,

friends: u8

}

impl Player {

fn with_name(name: &str) -> Player {

Player {

name: name.to_string(),

iq: 100,

friends: 100

}

}

fn get_friends(&self) -> u8 {

self.friends

}

fn set_friends(&mut self, count: u8) {

self.friends = count;

}

}

fn main() {

let mut player = Player::with_name("Dave");

player.set_friends(23);

println!("{}'s friends count: {}", player.name, player.get_friends());

let _ = Player::get_friends(&player);

}

with_name被称为关联方法,它没有使用self作为首个参数,类似于其它编程语言中的静态方法。通常是在类型自身上用::调用而不需要通过类的实例。

let player = Player::with_name("Dave");

get_friends和set_friends被称为实例方法,只能在已创建的实例上用.调用。

player.get_friends();

集合

数组

数组[T;N]具有固定的长度,可以存储相同类型的元素,T是元素的数据类型,N是一个表示长度的usize类型字面量。

let numbers: [u8;10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

println!("Number: {}", numbers[5]); // Number: 5

访问数组中的成员使用[index]。

元组

元组是异构集合,可以存储不同类型的元素,通常作为函数的返回值。

let num_and_str: (u8, &str) = (42, "hello");

let (n, s) = num_and_str;

println!("Num: {}, Str: {}", n, s);

向量

向量是一个动态长度的存储相同类型元素的集合,分配在堆上,既可以用Vec::new()创建,也可以用vec![]创建。

let mut numbers_vec: Vec = Vec::new();

numbers_vec.push(1);

numbers_vec.push(2);

let mut vec_with_macro = vec![1];

vec_with_macro.push(2);

let _ = vec_with_macro.pop();

let message = if numbers_vec == vec_with_macro {

"They are equal"

} else {

"Nah! They look different to me"

};

println!("{} {:?} {:?}", message, numbers_vec, vec_with_macro);

push()将元素追加到向量的末尾,pop()移除并返回末尾的元素。

哈希表

哈希表来自std::collections模块,用于存储键值对,使用HashMap::new创建。

use std::collections::HashMap;

fn main() {

let mut fruits = HashMap::new();

fruits.insert("apple", 3);

fruits.insert("mango", 6);

fruits.insert("orange", 2);

fruits.insert("avocado", 7);

for (k, v) in &fruits {

println!("I got {} {}", v, k);

}

fruits.remove("orange");

let old_avocado = fruits["avocado"];

fruits.insert("avocado", old_avocado + 5);

println!("\nI now have {} avocados", fruits["avocado"]);

}

切片

切片是指向现有集合类型的其它变量所拥有的连续区间,用&[T]表示。

let mut numbers: [u8; 4] = [1, 2, 3, 4];

{

let all: &[u8] = &numbers[..];

println!("All of them: {:?}", all);

}

{

let first_two: &mut [u8] = &mut numbers[0..2];

first_two[0] = 100;

first_two[1] = 99;

println!("Look ma! I can modify through slices: {:?}", numbers);

}

精彩链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: