Rust学习笔记01-基础知识

概述

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

基本数据类型

  • bool类型用true表示真,false表示假。
  • char表示单个字符。
  • 整数,根据位宽和是否有符号分为i8i16i32i64i128u8u16u32u64u128isizeusize
  • 浮点数根据位宽分为f32f64
  • [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类型,personnamasteString类型的。Rust 的字符串均为 UTF-8 编码。

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

条件和判断

if

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

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表示从09的整数序列,0..=10表示从010的整数序列。

自定义数据类型

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_friendsset_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<u8> = 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);
}
  • 27
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值