Rust程序设计语言
最近因为facebook开源了libra,整体是用rust写的,为了快速入门rust,我搭建了一个 Rust快速入门文档,欢迎访问
最近因为facebook开源了libra,整体是用rust写的,为了快速入门rust,我搭建了一个 Rust快速入门文档,欢迎访问
转载于:https://www.cnblogs.com/ctgulong/p/11104234.html
分享Rust编程语言的相关知识笔记,持续更新中!
本章简单介绍Rust语言的基本介绍,优点,环境配置,和第一个程序,学习更多基础内容请关注后续笔记!
基础入门学习参考资料:
一门赋予每个人构建可靠且高效软件能力的语言。 --官方给出的介绍
从上面官方介绍我们不难发现它的优点肯定有两点,可靠和高效!
Rust的优势决定了它能够胜任系统底层/系统中层/系统上层开发!
如果你是Mac系统,安装教程和Linux系统的安装教程一样,如果你是Windows系统,可以参考官网安装教程以及Rust程序设计语言(简体中文版)里面的安装教程!
打开终端输入:curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
选择默认回车,或者选择1)
等待安装完成
上述方法是采用官方安装,但是存在安装慢的问题,我们可以先换源然后再进行安装
# 先配置国内源
echo "export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static" >> ~/.bashrc
echo "export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup" >> ~/.bashrc
source ~/.bashrc
# 再安装
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
安装完成后:
终端输入rustc --version
查看是否安装成功
# 更新
rustup update
# 卸载
rustup self uninstall
第一个程序不来一个HelloWorld怎么行!
我们目前就直接演示在终端使用,后面我们会详细介绍cargo以及用clion写Rust
# 打开终端
# 在当前目录下创建一个包
mkdir hello_world
# 创建Rust源文件(Rust源文件扩展名为.rs)
cd hello_world
vim main.rs
# 输入代码
fn main(){
println!("Hello,World!")
}
# 保存退出后编译文件
rustc main.rs
# 运行文件
./main
#终端打印出了HelloWorld
====================
注意:
1. 在 Windows 上,运行文件输入命令 .\main.exe,而不是 ./main:
2. 在Rust中缩进是四个空格,而不是一个制表符Tab
3. 大部分代码以分号表示结尾
====================
Cargo 是 Rust 的构建系统和包管理器,我们用Cargo来管理Rust项目!
检查Cargo是否安装
终端输入:cargo --version
出现了Cargo版本号,则说明已经安装成功了,如果出现notfound等一些错误,则可以查看对应的解决方法!
创建项目
终端输入:cargo new 项目名
新建了hello_cargo目录,我们进入hello_cargo目录,生成了一个Cargo.toml文件(这是Cargo的配置文件)以及一个src目录,进入src目录我们可以看到还有一个main.rs文件
打开main.rs,可以看到已经给我们生成了一个Hello,World程序!
编译和运行
首先回到hello_cargo的项目目录下
终端输入:cargo build
进行编译
编译完成后再看目录下就有了一个Cargo.lock文件和target目录
Cargo.lock这个文件记录项目依赖的实际版本
target/debug/hello_cargo则是生成的项目可执行文件,我们直接在终端输入以下命令即可运行程序:
./target/debug/hello_cargo
# 或者在 Windows 下为 .\target\debug\hello_cargo.exe
# 控制台打印以下内容
Hello, world!
终端输入: cargo run
进行编译和运行
Cargo 还提供了一个叫 cargo check
的命令。该命令快速检查代码确保其可以编译,但并不产生可执行文件
入门介绍就到此为止,自己到手操作一下吧!学习更多基础内容请关注后续笔记!
第二章中提到过,变量默认是不可改变的(immutable)。
当变量不可变时,一旦值被绑定一个名称上,你就不能改变这个值。
在尝试改变预设为不可变的值时,产生编译时错误是很重要的,因为这种情况可能导致 bug。如果一部分代码假设一个值永远也不会改变,而另一部分代码改变了这个值,第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 的起因难以跟踪,尤其是第二部分代码只是 有时 会改变值。
Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要追踪一个值如何和在哪可能会被改变,从而使得代码易于推导。
不过可变性也是非常有用的。变量只是默认不可变;正如在第二章所做的那样,你可以在变量名之前加 mut 来使其可变。除了允许改变值之外,mut 向读者表明了其他代码将会改变这个变量值的意图。
除了防止出现 bug 外,还有很多地方需要权衡取舍。例如,使用大型数据结构时,适当地使用可变变量,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的编程风格,可能会使代码更易理解,为可读性而牺牲性能或许是值得的。
不允许改变值的变量,可能会使你想起另一个大部分编程语言都有的概念:常量(constants)。类似于不可变变量,常量是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。
这是一个声明常量的例子,它的名称是 MAX_POINTS,值是 100,000。(Rust 常量的命名规范是使用下划线分隔的大写字母单词,并且可以在数字字面值中插入下划线来提升可读性):
const MAX_POINTS: u32 = 100_000;
在声明它的作用域之中,常量在整个程序生命周期中都有效,这使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。
将遍布于应用程序中的硬编码值声明为常量,能帮助后来的代码维护人员了解值的意图。如果将来需要修改硬编码值,也只需修改汇聚于一处的硬编码值。
隐藏与将变量标记为 mut 是有区别的。当不小心尝试对变量重新赋值时,如果没有使用 let 关键字,就会导致编译时错误。通过使用 let,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不变的。
mut 与隐藏的另一个区别是,当再次使用 let 时,实际上创建了一个新变量,我们可以改变值的类型,但复用这个名字。
在 Rust 中,每一个值都属于某一个 数据类型(data type),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)和复合(compound)。
Rust 是 静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的类型。
标量(scalar)类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。
复合类型(Compound types)可以将多个值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。
元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定:一旦声明,其长度不会增大或缩小。
我们使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了可选的类型注解:
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
另一个包含多个值的方式是 数组(array)。与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,因为 Rust 中的数组是固定长度的:一旦声明,它们的长度不能增长或缩小。
Rust 中,数组中的值位于中括号内的逗号分隔的列表中:
fn main() {
let a = [1, 2, 3, 4, 5];
}
当你想要在栈(stack)而不是在堆(heap)上为数据分配空间(第四章将讨论栈与堆的更多内容),或者是想要确保总是有固定数量的元素时,数组非常有用。但是数组并不如 vector 类型灵活。vector 类型是标准库提供的一个 允许 增长和缩小长度的类似数组的集合类型。当不确定是应该使用数组还是 vector 的时候,你可能应该使用 vector。第八章会详细讨论 vector。
Rust使用 fn
关键字来声明新函数。
Rust 代码中的函数和变量名使用 snake case 规范风格。在 snake case 中,所有字母都是小写并使用下划线分隔单词。这是一个包含函数定义示例的程序:
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
Rust 中的函数定义以 fn 开始并在函数名后跟一对圆括号。大括号告诉编译器哪里是函数体的开始和结尾。
源码中 another_function 定义在 main 函数 之后;也可以定义在之前。
Rust 不关心函数定义于何处,只要定义了就行。
函数也可以被定义为拥有 参数(parameters),参数是特殊变量,是函数签名的一部分。
当函数拥有参数(形参)时,可以为这些参数提供具体的值(实参)。
parameter:形参,函数的参数
argument:实参,调用函数时传入的具体值
下面被重写的 another_function 版本展示了 Rust 中参数是什么样的:
fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("The value of x is: {}", x);
}
在函数签名中,必须 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解,意味着编译器不需要你在代码的其他地方注明类型来指出你的意图。
函数体由一系列的语句和一个可选的结尾表达式构成。目前为止,我们只介绍了没有结尾表达式的函数,不过你已经见过作为语句一部分的表达式。因为 Rust 是一门基于表达式(expression-based)的语言,这是一个需要理解的(不同于其他语言)重要区别。其他语言并没有这样的区别,所以让我们看看语句与表达式有什么区别以及这些区别是如何影响函数体的。
语句(Statements)是执行一些操作但不返回值的指令。
表达式(Expressions)计算并产生一个值。
语句不返回值。
因此,不能把 let 语句赋值给另一个变量,比如下面的例子尝试做的,会产生一个错误:
fn main() {
let x = (let y = 6);
}
let y = 6 语句并不返回值,所以没有可以绑定到 x 上的值。这与其他语言不同,例如 C 和 Ruby,它们的赋值语句会返回所赋的值。在这些语言中,可以这么写 x = y = 6,这样 x 和 y 的值都是 6;Rust 中不能这样写。
表达式会计算出一些值,并且你将编写的大部分 Rust 代码是由表达式组成的。
考虑一个简单的数学运算,比如 5 + 6,这是一个表达式并计算出值 11。
表达式可以是语句的一部分:在示例 3-1 中,语句 let y = 6; 中的 6 是一个表达式,它计算出的值是 6。
函数调用是一个表达式。
宏调用是一个表达式。
我们用来创建新作用域的大括号(代码块),{},也是一个表达式,例如:
fn main() {
let x = 5;
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y);
}
这个表达式:
{
let x = 3;
x + 1
}
是一个代码块,它的值是 4。这个值作为 let 语句的一部分被绑定到 y 上。注意结尾没有分号的那一行 x+1,与你见过的大部分代码行不同。
表达式的结尾没有分号。如果在表达式的结尾加上分号,它就变成了语句,而语句不会返回值。
函数可以向调用它的代码返回值。
我们并不对返回值命名,但要在箭头(->)后声明它的类型。
在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。
使用 return 关键字和指定值,可从函数中提前返回;但大部分函数隐式的返回最后的表达式。
这是一个有返回值的函数的例子:
fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x);
}
在 five 函数中没有函数调用、宏、甚至没有 let 语句——只有数字 5。
这在 Rust 中是一个完全有效的函数。注意,也指定了函数返回值的类型,就是 -> i32。
让我们看看另一个例子:
fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x);
}
fn plus_one(x: i32) -> i32 {
x + 1
}
运行代码会打印出 The value of x is: 6。但如果在包含 x + 1 的行尾加上一个分号,把它从表达式变成语句,我们将看到一个错误
fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x);
}
fn plus_one(x: i32) -> i32 {
x + 1;
}
运行代码会产生一个错误,如下:
error[E0308]: mismatched types
--> src/main.rs:7:28
|
7 | fn plus_one(x: i32) -> i32 {
| ____________________________^
8 | | x + 1;
| | - help: consider removing this semicolon
9 | | }
| |_^ expected i32, found ()
|
= note: expected type `i32`
found type `()`
主要的错误信息,“mismatched types”(类型不匹配),揭示了代码的核心问题。函数 plus_one 的定义说明它要返回一个 i32 类型的值,不过语句并不会返回值,使用空元组 () 表示不返回值。
因为不返回值与函数定义相矛盾,从而出现一个错误。
Rust 代码中最常见的用来控制执行流的结构是 if 表达式和循环。
if 表达式允许根据条件执行不同的代码分支。你提供一个条件并表示 “如果条件满足,运行这段代码;如果条件不满足,不运行这段代码。”
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
所有的 if 表达式都以 if 关键字开头,其后跟一个条件。在这个例子中,条件检查变量 number 的值是否小于 5。
条件 必须 是 bool 值。如果条件不是 bool 值,我们将得到一个错误。
因为 if 是一个表达式,我们可以在 let 语句的右侧使用它,例如在示例 3-2 中:
fn main() {
let condition = true;
let number = if condition {
5
} else {
6
};
println!("The value of number is: {}", number);
}
示例 3-2:将 if 表达式的返回值赋给一个变量
number 变量将会绑定到表示 if 表达式结果的值上。
记住,代码块的值是其最后一个表达式的值,而数字本身就是一个表达式。在这个例子中,整个 if 表达式的值取决于哪个代码块被执行。这意味着 if 的每个分支的可能的返回值都必须是相同类型;
Rust 有三种循环:loop、while 和 for。我们每一个都试试。
如果将返回值加入你用来停止循环的 break 表达式,它会被停止的循环返回:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
在循环之前,我们声明了一个名为 counter 的变量并初始化为 0。
接着声明了一个名为 result 来存放循环的返回值。
在循环的每一次迭代中,我们将 counter 变量加 1,接着检查计数是否等于 10。
当相等时,使用 break 关键字返回值 counter * 2。
循环之后,我们通过分号结束赋值给 result 的语句。
最后打印出 result 的值,也就是 20。
在程序中计算循环的条件也很常见。当条件为真,执行循环。当条件不再为真,调用 break 停止循环。
Rust 为此内置了一个语言结构,它被称为 while 循环。
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number = number - 1;
}
println!("LIFTOFF!!!");
}
这种结构消除了很多使用 loop、if、else 和 break 时所必须的嵌套,这样更加清晰。当条件为真就执行,否则退出循环。
作为更简洁的替代方案,可以使用 for 循环来对一个集合的每个元素执行一些代码。
更为重要的是,我们增强了代码安全性,并消除了可能由于超出数组的结尾或遍历长度不够而缺少一些元素而导致的 bug。
for 循环的安全性和简洁性使得它成为 Rust 中使用最多的循环结构。
即使是在想要循环执行代码特定次数时,例如示例 3-3 中使用 while 循环的倒计时例子,大部分 Rustacean 也会使用 for 循环。这么做的方式是使用 Range,它是标准库提供的类型,用来生成从一个数字开始到另一个数字之前结束的所有数字的序列。
下面是一个使用 for 循环来倒计时的例子,它还使用了一个我们还未讲到的方法,rev,用来反转 range:
fn main() {
for number in (1..4).rev() {
println!("{}!", number);
}
println!("LIFTOFF!!!");
}
use std::collections::HashMap;
fn main() {
let mut nums: Vec<i32> = vec![23, 55, 55, 67, 45, 32, 54];
println!("{:?}", nums);
let nums = sort(&mut nums);
println!("{:?}", nums);
println!("average: {}", average(nums));
println!("median: {}", median(nums));
println!("mode: {}", mode(nums));
}
fn average(vec: &Vec<i32>) -> f32 {
let mut r = 0;
for num in vec.iter() {
r += num;
}
r as f32 / vec.len() as f32
}
fn median(vec: &mut Vec<i32>) -> f32 {
let len = vec.len();
let sorted = sort(vec);
if len % 2 == 0 {
return (sorted[len / 2 - 1] as f32 + sorted[len] as f32) / (2 as f32);
}
sorted[len / 2] as f32
}
fn sort(vec: &mut Vec<i32>) -> &mut Vec<i32> {
for i in 0..vec.len() - 1 {
for j in 0..vec.len() - 1 - i {
if vec[j] >= vec[j + 1] {
let tmp = vec[j];
vec[j] = vec[j + 1];
vec[j + 1] = tmp;
}
}
}
return vec;
}
fn mode(vec: &Vec<i32>) -> i32 {
let mut num_times_map: HashMap<i32, i32> = HashMap::new();
for &num in vec.iter() {
let count = num_times_map.entry(num).or_insert(0);
*count += 1;
}
let mut mode_num: (i32, i32) = (0, 0);
for (num, times) in num_times_map.iter() {
if *times > mode_num.1 {
mode_num.1 = *times;
mode_num.0 = *num;
}
}
return mode_num.0;
}
执行结果:
use std::collections::HashMap;
use std::io::stdin;
fn main() {
let mut human_resource_map: HashMap<String, Vec<String>> = HashMap::new();
loop {
let mut input = String::new();
println!("输入Add {{员工}} to {{部门}}");
if let Result::Ok(_len) = stdin().read_line(&mut input) {
let words: Vec<&str> = input.trim().split_whitespace().collect();
if words[0] != "Add" || words[2] != "to" {
println!("输入错误,请重新输入!");
continue;
}
let department = String::from(words[3]);
let employee = String::from(words[1]);
let employees_in_this_department = human_resource_map.entry(department).or_insert(Vec::new());
employees_in_this_department.push(employee);
println!("{:#?}", human_resource_map);
} else {
println!("Input error");
}
}
}
执行结果: