一年终于更新了
Rust学习计划表
1. 环境搭建
2. 写个hello word
3. 学学基础
点击蓝字关注我哦~
Rust的代码组织
-
代码组织主要包括: -
哪些细节可以暴露,哪些细节是私有的 -
作用域内哪些名称有效
私有边界
Rust中所有的条目(函数、方法、Struct、enum、模块、常量)默认是私有的 模块不仅可以组织代码,还可以定义私有边界 如果想把函数或struct等设为私有,可以将它放到某个模块中 父级模块无法访问子级模块中的私有条目 子模块里可以使用所有祖先模块中的条目
pub关键字
使用pub关键字来将某些条目标记为公共的 只有标记为公共的,其他代码才可以使用,私有的条目只有同级作用域可以用
use关键字
可以使用use关键字将路径导入到作用域内,仍需遵循私有性原则 引入规则:
-
函数:将函数的父级模块引入作用域(指定到父级) -
struct,enum,其他:指定完整路径(指定到本身) -
同名条目:指定到父级
mod front_of_house{
pub mod hosting{
pub fn add_to_waitlist(){}
}
}
// 使用绝对路径
//use crate::front_of_house::hosting;
// 使用相对路径
use front_of_house::hosting;
use std::collectings::Hashmap;
pub fn eat_at_restaurant(){
hosting::add_to_waitlist();
let mut map = HashMap::new();
map.insert(1,2);
}
as关键字
as关键字可以为引入的路径指定本地的别名
use std::fmt::Result;
use std::io::Result as IoResult;
fn f1()-> IoResult{
}
使用pub use重新导出名称
使用use将路径名称导入到作用域后,该名称在此作用域内是私有的 pub use:重导出
-
将条目引入作用域 -
该条目可以被外部代码引入到它们的作用域
pub use std::fmt::Result;
使用嵌套路径清理大量的use语句
如果使用同一个包或模块下的多个条目
use std::io;
use std::fmt;
可以使用嵌套路径在同一行内将上述条目进行引入
-
路径相同的部分::{路径差异的部分,多个之间用逗号分隔},self表示{}前的路径,如std::io = std::io{self}
use std::{io,fmt}
通配符*
使用*
可以把路径中所有的公共条目都引入到作用域
use std::*
注意:谨慎使用 应用场景:
-
测试:将所有被测试代码导入到test模块 -
有时被用于预导入模块
使用外部包
-
Cargo.toml添加依赖的包(package),Cargo会自动去https://cratrs.io/ 网站去下载对应的依赖文件 -
use将特定 条目引入到作用域 在cargo.toml文件的[dependencies]中添加所需导入的包名称及版本 -
-
vscode会自动下载对应的版本,并提示是否有最新的版本 -
-
标准库std也被当作外部包,但是不需要在cargo.toml中添加包含std,需要使用use将std中的特定条目引入当前作用域
★
如果下载特别慢或者下载失败,可以使用命令行下载
”
在命令行中输入cargo build命令,会出现如下的报错:Blocking waiting for file lock on package cache
进入cargo的安装目录,查看隐藏文件,将.package-cache文件删除
然后再允许cargo build命令即可重新下载
添加国内镜像源
进入cargo所在文件夹,创建config文件 添加如下的内容
[source.crates-io]
registry = "http://github.com/rust-lang/crates.io-index"
replace-with = 'tuna'
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
[net]
git-fetch-with-cli = true
[http]
//解决SSL报错问题
check-revoke = false
然后重新build即可
rust模块系统
Rust 提供了一系列功能,可帮助你管理和组织代码。这些功能称为“Rust 模块系统”。系统由箱(Crate)、模块(Module)、路径(Path)和工具组成,以与那些项结合使用。
-
package(包):Cargo的特性,让你构建、测试、共享Crate -
Crate(单元包):一个模块树,它可以产生一个library(库)或可执行文件 -
Rust Crate是一个编译单元。它是 Rust 编译器可以运行的最小代码段。Crate中的代码一起编译以创建二进制可执行文件或库。在 Rust 中,仅将Crate编译为可重复使用的单元。Crate包含具有隐式未命名顶级模块的 Rust 模块的层次结构。 -
Module(模块)、Use :让你控制代码的组织、作用域、私有路径 -
Rust 模块通过让你管理箱内单个代码项的范围来帮助你组织程序。结合使用的相关代码项或项可以分组到相同模块中。递归代码定义可以跨越其他模块。 -
Path(路径):为struct、function或module等项命名的方式 -
在 Rust 中,可以使用路径来命名代码中的项。例如,路径可以是一个数据定义(例如,矢量、代码函数,甚至是模块)。模块功能还可帮助你控制路径的隐私。可以指定可公开访问的代码部分和私有部分。通过该功能可以隐藏实现详细信息
Crate
Crate的类型
Crate一共有两种类型,一种是二进制文件,一种是library,及库文件
Crate Root:
-
是源代码文件 -
Rust编译器从这里开始,组成你的Crate的根Module
一个Package
-
一个Package只包含一个Cargo.toml,它描述了如何构建这些Crates -
只能包含0-1个 library crate -
可以包含任意数量的二进制文件(Binary crate) -
但是至少包含一个Crate(library或binary) -
一个package可以同时包含src/main.rs和src/lib.rs -
一个binary crate,一个library crate -
名称与package名相同 -
一个package可以有多个binary crate: -
文件都放在src/bin -
每个文件都是单独的binary crate
rust package程序结构
以使用cargo new创建的程序结构进行分析:
文件结构
上面的命令运行后,会创建一个新的目录,目录名与项目名一致,目录结构如下
-
src下存放的程序源代码,所有源码文件后缀都是rs -
cargo生成的main.rs在src目录下 -
会初始化一个.gitignore的git仓库 -
创建一个Cargo.toml的配置文件,是cargo的配置格式 -
-
name,项目名称 -
version,项目版本 -
authors,项目作者 -
edittion,使用的rust版本 -
[package] 是一个区域标题,表示下发的内容是用来配置package的 -
[dependencies],另一个区域的开始,会列出项目的依赖项 -
在rust中,代码的包称作crate。 -
顶层目录可以放置:README、许可证信息、配置文件和其他与程序源码无关的文件 -
src/main.rs: -
binary crate 的crate root -
crate名与package名相同 -
src/lib.rs: -
package 包含一个library crate -
library crate的crate root -
crate名与package名相同 -
Cargo把crate root文件交给rustc来构建library或binary
文件命名
程序文件后缀名:rs 文件命名规范:hello_word.rs
代码缩进
在函数体中,大多数代码语句以分号 ;
结尾。Rust 按顺序依次处理这些语句。当代码语句不以分号结尾时,Rust 知道必须执行下一行代码才能完成起始语句。
为了帮助查看代码中的执行关系,请使用缩进。此格式可显示代码的组织方式以及揭示完成功能任务的步骤流程。起始代码语句从左边距缩进四个空格。当代码不以分号结尾时,要执行的下一行代码再缩进四个空格。
下面是一个示例:
fn main() {// 函数声明没有缩进
// 函数体的第一步
// 子步骤: 在第一步完成之前执行
// 函数体的第二步
// 子步骤A: 在第二步之前执行完成
// 子步骤B: 在第二步之前执行完成
// 子步骤1: 在子步骤B完成之前执行
// 函数体中的第三步,依此类推...
}
Crate的作用
将相关功能组合到一个作用域内,便于在项目间进行共享
-
防止命名冲突 例如 Rand crate,访问它的功能需要通过它的名字rand
Module
-
在一个crate内,将代码进行分组 -
增加可读性,易于复用 -
控制项目(item)的私有性。public、private
定义module
可以控制作用域和私有性建立module:
-
mod 名称{} -
module是可嵌套的 -
可包含其它项(struct、enum、常量、trait、函数等)的定义
mod front_of_hose{
mod hosting{
fn add_to_waitlist(){}
fn seat_at_table(){}
}
mod serving{
fn take_order(){}
}
}
将模块内容移动到其他文件
模块定义是,如果模块名后面是;而不是代码块
-
rust会从与模块同名的文件中加载内容 -
模块树的结构不会变化例子 源代码 -
-
拆分后 -
-
随着模块逐渐变大,该技术可以让把模块的内容移动到其他文件中
path 路径
为了在Rust的模块中找到某个条目,需要使用路径 路径的两种形式:
-
绝对路径:从 crate root开始,使用crate名或字面值crate -
相对路径:从当前模块开始,使用self,super或当前模块的标识符 路径至少由一个标识符组成,标识符之间使用::
mod front_of_hose{
pub mod hosting{
pub fn add_to_waitlist(){}
fn seat_at_table(){}
}
mod serving{
fn take_order(){}
}
}
pub fn eat_at_restaurant(){
creat::front_of_hose::hosting::add_to_waitlist()
front_of_hose::hosting::add_to_waitlist()
}
super关键字
super关键字:用来访问父级模块路径中的内容,类似文件系统中的..
fn serve_order(){}
mod back_of_house{
fn fix_incorrect_order(){
cook_order();
super::serve_order();
}
fn cook_order(){}
}
使用 Rust 箱和库
Rust 标准库 std
包含 Rust 程序中的基本定义和操作的可重复使用代码。该库拥有核心数据类型(例如 String
和 Vec<T>
)的定义、Rust 基元的操作、常用宏函数的代码、对输入和输出操作的支持以及许多其他功能区域。
有数以万计的库和第三方箱可用于 Rust 程序,其中大部分可以通过 Rust 的第三方箱存储库 crates.io 进行访问。稍后你将了解如何从项目访问这些箱,但以下是编程练习中使用的一些箱:
-
std - Rust 标准库。在 Rust 练习中,你将会注意到以下模块: -
std::collections - 集合类型的定义,如 HashMap
。 -
std::env - 用于处理环境的函数。 -
std::fmt - 控制输出格式的功能。 -
std::fs - 用于处理文件系统的功能。 -
std::io - 用于处理输入/输出的定义和功能。 -
std::path - 支持处理文件系统路径数据的定义和功能。 -
structopt - 用于轻松分析命令行参数的第三方箱。 -
chrono - 用于处理日期和时间数据的第三方箱。 -
regex - 用于处理正则表达式的第三方箱。 -
serde - 适用于 Rust 数据结构的序列化和反序列化操作的第三方箱。
默认情况下,std
库适用于所有 Rust 箱。若要访问箱或库中的可重复使用代码,请使用 use
关键字。通过 use
关键字,箱或库中的代码就会“进入范围”,这样你就可以访问程序中的定义和功能。标准库是在路径 std
的 use
语句中访问的,如 use std::fmt
所示。其他箱或库是通过其名称访问的,例如 use regex::Regex
。
使用 Cargo 创建和管理项目
虽然可以直接使用 Rust 编译器 (rustc
) 来生成箱,但大多数项目都使用 Rust 生成工具和名为 Cargo 的依赖项管理器。
Cargo 可以为你做许多事情,包括:
-
使用 cargo new
命令创建新的项目模板。 -
使用 cargo build
编译项目。 -
使用 cargo run
命令编译并运行项目。 -
使用 cargo test
命令测试项目。 -
使用 cargo check
命令检查项目类型。 -
使用 cargo doc
命令编译项目的文档。 -
使用 cargo publish
命令将库发布到crates.io
。 -
通过将箱的名称添加到 Cargo.toml 文件来将依赖箱添加到项目。
编译与运行rust程序
rustc
rustc只适合简单的rust程序
编译和运行是单独的两个步骤:
-
运行Rust程序之前必须先编译,命令未rustc 源文件名 -
编译:rust main.rs,编译完成后会生成exe文件 -
编译成功后,会生成一个二进制文件 -
在windows系统上还会生成一个.pdb文件,里面包含调试信息 -
Rust是ahead-of-time编译的语言: -
可以先编译程序,然后把编译好的可执行文件交给别人运行(无需安装rust) -
运行: -
windows:.main.exe -
Linux:./main
Cargo
-
Cargo是Rust的构建系统和包管理工具 -
构建代码、下载依赖的库、构建这些库 -
安装rust的时候会自动安装好Cargo 如果创建项目时没有使用cargo,也可以把项目转化为使用cargo -
把源代码文件移动到src目录下 -
在cargo.toml配置中添加相关的信息
使用cargo 创建项目
创建普通项目
cargo new 项目名
使用cargo构建library项目
cargo new 项目名 --lib
使用cargo构建项目
cargo build
创建可执行文件:targetdebughello_cargo或targetdebughello_cargo.exe(windows) 第一次运行cargo build 会在项目顶层目录生成cargo.lock文件
-
该文件负责追踪项目依赖的精确版本 -
不需要手动修改该文件
构建并运行cargo项目
cargo run
如果之前编译成功过,并且源码没有改变,那么就会直接运行二进制文件。如果没有编译过,或修改了源码,则会先编译成二进制文件,然后运行二进制文件。
使用cargo检查代码
cargo check
检查代码,确保能通过编译,但是不产生任何可执行文件
-
cargo check要比cargo build快得多 -
编写代码的时候可以连续反复的使用cargo check检查代码,提高效率
编译发布版本
cargo build --release
-
编译时会进行优化 -
代码会运行的更快,但是编译时间会更长 -
会在target/release而不是target/debug生成可执行文件 -
存在两种配置: -
一个开发使用 -
一个发布使用
特殊语句
debug语句
在上面的示例中,查找以下代码语句。代码的多个位置使用了该语句。
// Set the Debug flag so we can check the data in the output
#[derive(Debug)]
通过 #[derive(Debug)]
语法可以在代码执行期间查看某些在标准输出中无法查看的值。要使用 println!
宏查看调试数据,请使用语法 {:#?}
以可读的方式格式化数据。
{}参数的值替换
在 Rust Learn 模块课程中,我们经常使用参数列表调用 println!
宏,该参数列表包括使用大括号 {}
实例括起来的文本字符串和其他值。 println!
宏将文本字符串中的每个大括号 {}
实例替换为列表中下一个参数的值。示例:
fn main() {
// 调用println!具有三个参数: 字符串,值,值
println!("英文字母表的第一个字母是{},最后一个字母是{}.", 'A', 'Z');
}
我们使用三个参数调用 println!
宏:字符串、值和其他值。宏按顺序处理参数。文本字符串中的每个大括号 {}
实例都替换为列表中下一个参数的值。
rust 宏
todo! 宏
在 Rust 模块中练习时,你会注意到示例代码通常使用 todo!
宏。Rust 中的宏类似于采用可变数量的输入参数的函数。 todo!
宏用于标识 Rust 程序中未完成的代码。宏有助于原型制作,或者当你想要指示未完成的行为时。
下面是练习中如何使用 todo!
宏的示例:
fn main() {
// 显示消息 “Hello,world!”
todo!("使用println!() 宏显示消息");
}
编译使用todo!宏的代码时,编译器可能会返回一条需要查找已完成功能的紧急消息:
println!宏
main
函数执行一项任务。该函数调用在 Rust 中预定义的 println!
宏。 println!
宏需要一个或多个输入参数,这些参数会显示在屏幕或标准输出中。在示例中,我们将一个输入参数(即文本字符串“Hello, world!”)传递给宏
fn main() {
// 我们的主要功能执行一项任务: 显示一条消息
// println!在屏幕上显示输入 “Hello,world!”
println!("Hello, world!");
}
println!()宏中可以使用{}占位符输出变量的内容,本质是调用了变量的Display方法,但是如果目标类型没有实现Display的方法,则不能使用{}占位符进行直接输出
可以修改为使用{:?}或{:#?}进行打印输出,但是要先在定义的目录类型前添加debug声明
//定义长方形的struct
#[derive(Debug)]
struct Rectangle{
width: u32,
length: u32,
}
println!("{:?}", rect);
rust中的函数
函数是执行特定任务的代码块。我们根据任务将程序中的代码分割成块。通过分割,代码变得更易于理解和维护。在为任务定义函数后,可以在需要执行相应任务时调用该函数。
每个 Rust 程序都必须有一个名为 main
的函数。 main
函数中的代码始终是 Rust 程序中运行的第一个代码。我们可以从 main
函数内部或从其他函数内部调用其他函数。
fn main() {
println!("Hello, world!");
}
★
定义函数:fn main() {}
没有参数,没有返回值 main函数很特别:它是每个rust可执行程序最先运行的代码 打印文本:println!("hello word"); rust的缩进是4个空格而不是tab println!是一个rust macro(宏)
如果是函数的话,就没有!
“hello word”是字符串,是println!的参数
这行代码以;结尾 ”
声明函数
要在 Rust 中声明函数,必须使用 fn
关键字。在函数名称后面,告知编译器函数需要多少形参或实参作为输入。参数在括号 ()
内列出。函数体是执行函数任务的代码,在大括号 {}
内定义。最佳做法是将代码格式化,使函数体的左大括号紧跟在参数列表的括号后面。
-
Rust 函数的命名规范(snake case) -
所有的字母都是小写的,单词之间使用下划线分开
fn main() {
println!("Hello, world!");
goodbye();
}
fn goodbye() {
println!("Goodbye.");
}
我们使用函数名称以及括号中的输入参数来调用函数。如果函数没有任何输入参数,则将括号留空。在示例中,main
和 goodbye
函数都没有输入参数。
你可能已经注意到,我们在定义 main
函数后,定义了 goodbye
函数。我们本可以在定义 main
之前定义 goodbye
函数。Rust 不在意文件中函数的定义位置,只要在文件中的某处定义了函数即可。
查看签名
函数声明的第一部分称为函数签名。
示例中 goodbye
函数的签名具有以下特征:
-
fn
:Rust 中的函数声明关键字。 -
goodbye
:函数名称。 -
(message: &str)
:函数的实参或形参列表。一个指向字符串数据的指针应作为输入值。 -
-> bool
:箭头指向此函数将始终返回的值类型。
goodbye
函数接受一个字符串指针作为输入并输出一个布尔值。
函数参数
如果函数具有输入参数,请命名每个参数并在函数声明的开头指定数据类型。由于参数的命名方式与变量类似,因此可以访问函数体中的参数。
-
函数的签名里,必须指明每个参数的类型 -
多个参数之间使用逗号分开
fn add(x:i32, y:i32) ->i32{
x+y
}
让我们修改 goodbye
函数,以将指向某些字符串数据的指针作为输入参数。
fn goodbye(message: &str) {
println!("n{}", message);
}
fn main() {
let formal = "Formal: Goodbye.";
let casual = "Casual: See you later!";
goodbye(formal);
goodbye(casual);
}
通过使用两个不同的参数值从 main
函数调用声明的函数来对其进行测试,然后检查输出:
函数中的语句和表达式
-
函数体由一系列语句组成,可选的由一个表达式结束 -
Rust是一个基于表达式的语言 -
语句是执行一些动作的指令 -
表达式会计算产生一个值 -
函数的定义也是语句 -
语句不返回值,所以不可以使用let将一个语句赋值给一个变量 -
-
字面值和字面值的计算都是表达式,表达式可以返回值 -
-
注意函数的最后一行不能加;添加了;就表示为语句,语句不返回值,是一个空的元组,所以无法返回,不加;表达表达式,表达式可以返回值。 -
函数的返回值
-
在 - >符号后面声明函数的返回值的类型,但是不可以为返回值命名 -
在rust中,返回值就是函数体里面最后一个表达式的值 -
若想提前返回,需使用return关键字,并指定一个值 -
大多数函数都是默认使用最后一个表达式作为返回值
fn add(x:i32,y:i32) -> i32{
x+y
}
当函数返回某个值时,请在函数参数列表后面和函数体的左大括号前面添加语法 -> <type>
。箭头语法 ->
表示函数向调用方返回某个值。编译器通过 <type>
部分判断返回值的数据类型。
在 Rust 中,常见的做法是通过使函数中的最后一行代码等于要返回的值,从而在函数末尾返回一个值。以下示例显示此行为。 divide_by_5
函数将输入数除以 5 的结果返回到调用函数:
fn divide_by_5(num: u32) -> u32 {
num / 5
}
fn main() {
let num = 25;
println!("{} divided by 5 = {}", num, divide_by_5(num));
}
运行结果如下:
使用return关键字
可以在函数中的任意位置使用 return
关键字来停止执行并将值发送回调用方。通常,return
关键字与条件测试结合使用。
在下面的示例中,如果 num
的值为 0,则显式使用 return
关键字提前从函数返回:
fn divide_by_5(num: u32) -> u32 {
if num == 0 {
// Return early
return 0;
}
num / 5
}
显式使用 return
关键字时,语句以分号结束。如果在不使用 return
关键字的情况下发送回返回值,请勿以分号结束语句。你可能已注意到,我们没有将结束分号用于 num / 5
返回值语句。
提取函数消除重复的代码
原始代码,使用循环实现了找出list中最大的数
fn main(){
let number_list = vec![34,50,100,65];
let mut largest = number_list[0];
for number in number_list{
if number > largest{
largest = number;
}
}
println!("The largest number is {:?}", largest);
}
但是当有多个列表的时候,代码就会重复
fn main(){
let number_list = vec![34,50,100,65];
let mut largest = number_list[0];
for number in number_list{
if number > largest{
largest = number;
}
}
println!("number_list largest number is {:?}", largest);
let number1_list = vec![100,34,600,89,54,2,43,8];
let mut largest1 = number1_list[0];
for number in number1_list{
if number > largest1{
largest1 = number;
}
}
println!("number1_list largest number is {:?}", largest1);
}
重复代码的危害:
-
容易出错 -
需求变更时需要在多处进行修改 消除重复代码,提取公共部分定义函数
fn largest(list:&[i32])->i32{
let mut largest = list[0];
for &number in list{
if number > largest{
largest = number;
}
}
largest
}
fn main(){
let number_list = vec![34,50,100,65];
println!("number_list largest number is {:?}", largest(&number_list));
let number1_list = vec![100,34,600,89,54,2,43,8];
println!("number1_list largest number is {:?}", largest(&number1_list));
}
rust中的方法
-
方法和函数类似:fn 关键字、名称、参数、返回值 -
方法与函数的不同之处: -
方法是在struct或(enum、trait对象)的上下文中定义 -
方法的第一个参数是self,表示方法被调用的struct或(enum、trait对象)实例
定义方法
定义方法需要先有struct或(enum、trait对象)的声明,方法是和这些对象绑定在一起的,使用impl关键字进行声明方法,
-
方法的第一个参数是self,即对象实例&self,也可以获取其所有权或可变借用,和其他参数一样 -
方法的优点:可以更良好的组织代码
#[derive(Debug)]
struct Rectangle{
width: u32,
length: u32,
}
impl Rectangle{
fn area(&self) -> u32{
self.width * self.length
}
}
方法的调用
方法的调用使用点标记法,实例.方法名即可
方法调用的运算符
-
在C/C++中:Object->something()和(*object).something()一样 -
在Rust中,没有->运算符 -
但是Rust会自动引用或解引用 -
在调用方法时就会发生这种行为 -
在调用方法时,Rust会根据情况自动添加&、&mut、或*,以便object可以匹配方法的签名 -
下面两行代码效果相同 -
P1.distance(&p2) -
(&p1).distance(&p2)
方法的参数
方法同样可以拥有多个参数(除self外)
关联函数
可以在impl块里定义不把self作为第一个参数的函数,它们叫做关联函数(不是方法)
-
例如:String::from -
关联函数通常用于构造器 -
关联函数还可以用于模块创建的命名空间
创建关联函数
在impl块中定义关联函数
//定义长方形的struct
#[derive(Debug)]
struct Rectangle{
width: u32,
length: u32,
}
impl Rectangle {
// 计算长方形的面积
fn area(&self) -> u32{
self.width * self.length
}
// 定义一个正方形
fn square(size: u32) -> Rectangle{
Rectangle { width: size, length: size }
}
}
调用关联函数
使用实例::函数名进行调用
let s = Rectangle::square(10);
rust 中的注释
rust语言使用//做单行注释,多行注释使用/**
// 单行注释
/*
多行注释
*/
rust中还有一种文档注释,以后再说
rust中的变量
开发人员编写计算机程序来处理数据。收集、分析、存储、处理、共享和报告数据。我们使用变量将数据存储在命名引用中,稍后可在代码中引用这些数据。
声明变量
在 Rust 中,变量用关键字 let
声明。每个变量都有一个唯一的名称。声明变量后,可将其绑定到某个值,也可稍后在程序中绑定该值。以下代码声明名为 a_number
的变量。
let a_number;
a_number
变量尚未绑定到某个值。可修改此语句以将值绑定到变量:
let a_number = 10;
以下代码声明两个变量。声明第一个变量,但不绑定到某个值。声明第二个变量,并绑定到某个值。在后面的程序中,第一个变量的值绑定到某个字。代码调用 println!
宏来显示变量值。
fn main() {
// 声明一个变量
let a_number;
// 声明第二个变量并绑定值
let a_word = "Ten";
// 将值绑定到第一个变量
a_number = 10;
println!("The number is {}.", a_number);
println!("The word is {}.", a_word);
}
如果调用println!
宏并尝试在绑定 a_number
变量之前显示该变量的值,则编译器会返回错误,因为此时该变量还没有赋值
不变与可变
在Rust中,变量绑定默认不可变,如果变量不可变,在将值绑定到名称到,将无法改变此值。例如上面的代码,如果再次尝试修改a_number
的值,则会编译错误
如果要修改变量的值,则必须使用mut
关键字将变量绑定设为可变
let mut a_number;
添加mut
关键字后,已经可以修改变量的值了,所有不会报错,程序正常运行。
变量与常量
常量(constant),常量在绑定值以后也是不可变的,但是它与不可变的变量还是有很多的区别:
-
常量不可以使用mut关键之,常量永远都是不可以变的 -
声明常量使用const关键字,它的类型必须被标注 -
常量可以在任何作用域内进行声明,包括全局作用域 -
常量只可以绑定到常量表达式,无法绑定到函数的调用结果或只能运算时才能计算出的值 在程序运行期间,常量在其声明的作用域内一直有效 在rust中,常量的命名规范:常量使用全大写字母,每个单词之间用下划线分开 在rust中,数字可以添加下划线增加可读性,不影响数字大小,如100_000
变量隐藏(shadowing)
可以声明与某个现有变量同名的新变量。新的声明会创建新的绑定。在 Rust 中,此操作称为“隐藏”,这是由于新变量会隐藏上一个变量。旧变量仍存在,但无法再于此范围内引用它。shadow和把变量标记为mut是不一样的:
-
如果不使用let关键之,那么重新给非mut的变量赋值会导致编译时错误 -
而使用let声明的同名新变量,也是不可变的 -
使用let声明的同名新变量,它的类型可以与之前不同,但是使用let mut定义可变变量,类型不能改变 -
-
以下代码演示隐藏的用法。声明名为 shadow_num
的变量。我们没有将变量定义为可变变量,因为每个let
操作都会创建一个名为shadow_num
的新变量,同时隐藏以前的变量绑定。
fn main() {
// 声明第一个变量绑定,名称为 “shadow_num”
let shadow_num = 5;
// 声明第二个变量绑定,隐藏现有变量 “shadow_num”
let shadow_num = shadow_num + 5;
// 声明第三个变量绑定,隐藏变量 “shadow_num” 的第二个绑定
let shadow_num = shadow_num * 2;
println!("The number is {}.", shadow_num);
}
总结
-
Rust 程序的基本结构。 main
函数是所有 Rust 程序的入口点。println!
宏可用于显示变量值和显示程序进度。可以使用let
关键字定义变量。可以使用mut
关键字将这些值声明为不可变或可变(可更改)。 -
Rust 语言概念,包括许多主要数据类型和复合数据类型。你学习了如何处理整数和浮点数、字符和文本字符串以及布尔 true/false 值。Rust 语言严格解释数据类型。只有在正确定义和使用了数据类型时,程序才能成功编译和运行。 -
编写了一个函数,通过使用存储在 struct
和enum
中的数据来生成汽车。在示例程序中查找了todo!
宏的实例并完成了代码。使用了 Rust 操场来修改代码、编译程序并运行可执行文件。
了解详细信息
请访问以下链接,详细了解我们在本模块中探索的部分项目:
-
Rust 入门 -
了解经典的 C 编程结构类型 -
查看代数数据类型
Rust 参考文档
-
Rust 编程语言 -
查看 Rust 关键字
Rust:数据类型
-
使用数组 -
使用布尔值 -
使用字符 -
使用十进制数和浮点类型 -
使用枚举 -
使用整数类型 -
使用字符串 -
使用结构 -
使用元组
Rust:概念
-
了解变量、可变性和隐藏 -
了解函数 -
使用 println! 显示输出 宏 -
用 todo! 指示未完成的代码 宏
系列链接
1. 环境搭建
还有后续,学完慢慢发
end
扫码关注我哦
原文始发于微信公众号(宁雪):Rust基础知识
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论