没学过本语言的建议看官方文档,作者不建议看本文档进行学习,本文档适用于复习,本文为官方文档的精简版并加以改进。
rust安装: https://www.rust-lang.org/zh-CN/tools/install
Cargo 是 Rust 的构建系统和包管理器。
创建Rust 工程目录
cargo new xxx
Cargo 除了创建工程以外还具备构建(build)工程、运行(run)工程等一系列功能,构建和运行分别对应以下命令:
cargo build
cargo run
Cargo 还具有获取包、打包、高级构建等功能。
Rust 输出文字的方式主要有两种:println!() 和 print!()。前者会在输出的最后附加输出一个换行符。当用这两个"函数"输出信息的时候,第一个参数是格式字符串,后面是一串可变参数,对应着格式字符串中的"占位符"
fn main() {
let a = 12;
println!("a is {}", a);
}
//输出a is 12
rustc
编译rs文件rustc main.rs
./main
在 {} 之间可以放一个数字,它将把之后的可变参数当作一个数组来访问,下标从 0 开始。
println!("a is {0}, a again is {0}", a);
格式字符串
fn main() {
println!("{{内容}}");
}
//输出{内容}
use std::io; //prelude
fn main() {
println!("猜数");
println!("猜一个数");
//定义mut可变
let mut guess = String::new();
//最好加expect来判断
io::stdin().read_line(&mut guess).expect("无法读取行");
println!("你猜的数是{}",guess)
}
use rand::Rng; //trait
fn main() {
let secret_number = rand::thread_rng().gen_range(1..101);
println!("神秘数字是{}",secret_number);
}
use std::io; //prelude
use rand::Rng; //trait
use std::cmp::Ordering;
fn main() {
println!("猜数");
let secret_number = rand::thread_rng().gen_range(1..101);
println!("神秘数字是{}",secret_number);
loop {
println!("猜一个数");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("无法读取行");
let guess:u32 = match guess.trim().parse(){
Ok(num) => num,
Err(_) => continue,
};
println!("你猜的数是{}",guess);
match guess.cmp(&secret_number){
Ordering::Less => println!("猜的数比神秘数小"),
Ordering::Greater => println!("猜的数比神秘数大"),
Ordering::Equal =>{
println!("你赢了");
break;
},
}
}
}
声明了 a 为无符号 64 位整型变量
如果没有声明类型,a 将自动被判断为有符号 32 位整型变量
let a: u64 = 123;
声明a为可变变量。
let mut a = 123;
不可使用mut,
const MAX_POINTS:u32 = 100_00
可以使用相同名字声明新变量,新变量会shadow之前声明的同名变量。
let spaces = " "
let spaces = space.len();
// 单行
/*
多行
*/
Rust 中的函数定义以 fn
开始,后跟着函数名和一对圆括号。大括号告诉编译器函数体在哪里开始和结束。
风格:下划线命名法
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
参数的使用
fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("The value of x is: {}", x);
}
if
表达式注意:代码中if后面的条件必须是 bool
值。如果条件不是 bool
值,会报错。
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
else if
处理多重条件fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
Rust 有三种循环:loop
、while
和 for
。
使用 loop
重复执行代码,相当于python等语言中的while true
fn main() {
loop {
//重复执行此大括号内的代码
//可适当添加if控制结束
println!("again!");
}
}
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("LIFTOFF!!!");
}
for
循环的安全性和简洁性使得它成为 Rust 中使用最多的循环结构。
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {}", element);
}
}
长度 | 有符号类型 | 无符号类型 |
---|---|---|
8 位 | i8 |
u8 |
16 位 | i16 |
u16 |
32 位 | i32 |
u32 |
64 位 | i64 |
u64 |
128 位 | i128 |
u128 |
arch | isize |
usize |
数字字面量 | 示例 |
---|---|
十进制 | 98_222 |
十六进制 | 0xff |
八进制 | 0o77 |
二进制 | 0b1111_0000 |
字节 (仅限于 u8 ) |
b'A' |
比方说有一个 u8
,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256,则会发生整型溢出
要显式处理溢出的可能性,可以使用标准库针对原始数字类型提供的以下一系列方法:
wrapping_*
方法在所有模式下进行包裹,例如 wrapping_add
checked_*
方法时发生溢出,则返回 None
值overflowing_*
方法返回该值和一个指示是否存在溢出的布尔值saturating_*
方法使值达到最小值或最大值f32
类型是单精度浮点型,f64
为双精度浮点型。
基本数学运算:加法、减法、乘法、除法和取模运算
// addition
let sum = 5 + 10;
// subtraction
let difference = 95.5 - 4.3;
// multiplication
let product = 4 * 30;
// division
let quotient = 56.7 / 32.2;
let floored = 2 / 3; // Results in 0
// remainder
let remainder = 43 % 5;
true和false
let t = true;
let f: bool = false;
char
(字符)类型是该语言最基本的字母类型,大小为 4 个字节
一些合法值:标音字母,中文/日文/韩文的文字,emoji,还有零宽空格(zero width space)
元组中的每个位置都有一个类型,并且元组中不同值的类型不要求是相同的。
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
想从元组中获取个别值,我们可以使用模式匹配来解构(destructuring)元组的一个值
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y);
}
使用一个句点(.
)连上要访问的值的索引来直接访问元组元素。
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
没有任何值的元组 ()
是一种特殊的类型,该类型被称为单元类型(unit type),只有一个值,该值被称为单元值(unit value)
数组的每个元素必须具有相同的类型。Rust 中的数组具有固定长度。
fn main() {
let a = [1, 2, 3, 4, 5];
}
明确元素数量不需要改变时,数组会特别有用。但它们不像 vector (Rust 中动态数组)类型那么灵活。vector 类型类似于标准库中提供的集合类型,其大小允许增长或缩小。
let a: [i32; 5] = [1, 2, 3, 4, 5];
这里,i32
是每个元素的类型。分号之后,数字 5
表明该数组包含 5 个元素。
let a = [3; 5];
变量名为 a
的数组将包含 5
个元素,这些元素的值初始化为 3
。这种写法与 let a = [3, 3, 3, 3, 3];
效果相同。
访问数组元素
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}
栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 后进先出(last in, first out)
所有权解决问题:
需要复制String中堆上的数据需要使用clone方法
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
而在栈上的数据不需要使用clone,
Rust 有一个叫做 Copy
trait 的特殊标注,可以用在类似整型这样的存储在栈上的类型上。
如果一个类型实现了 Copy
trait,那么一个旧的变量在将其赋值给其他变量后仍然可用。
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
作为一个通用的规则,任何一组简单标量值的组合都可以实现 Copy
,任何不需要分配内存或某种形式资源的类型都可以实现 Copy
。如下是一些 Copy
的类型:
u32
。bool
,它的值是 true
和 false
。f64
。char
。Copy
的时候。比如,(i32, i32)
实现了 Copy
,但 (i32, String)
就没有。首先,让我们看一下所有权的规则。当我们通过举例说明时,请谨记这些规则:
返回值与作用域
函数在返回值的过程中也会发生所有权的转移
当一个包含heap数据的变量离开作用域,它的值就会被drop函数清理,除非数据所有权移动到另一个变量上。
fn main(){
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.",s1,len);
}
fn calculate_length(s:&String) -> usize {
s.len()
}
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
参数s类型需改为字符串
let my_string = String::from("hello world");
// `first_word` 接受 `String` 的切片,无论是部分还是全部
let word = first_word(&my_string[0..6]);
let word = first_word(&my_string[..]);
// `first_word` 也接受 `String` 的引用,
// 这等同于 `String` 的全部切片
let word = first_word(&my_string);
let my_string_literal = "hello world";
// `first_word` 接受字符串字面量的切片,无论是部分还是全部
let word = first_word(&my_string_literal[0..6]);
let word = first_word(&my_string_literal[..]);
// 因为字符串字面值**就是**字符串 slice,
// 这样写也可以,即不使用 slice 语法!
let word = first_word(my_string_literal);
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
使用user1中的一个值创建一个新的User实例
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}
fn main() {
// --snip--
let user1 = User {
email: String::from("[email protected]"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
let user2 = User {
email: String::from("[email protected]"),
..user1
};
}
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}
// fn main() {
// let w = 50;
// let h = 50;
// println!("{}",area(w, h));
// }
// fn area(width:u32,length:u32) -> u32 {
// width*length
// }
//改进
// fn main() {
// let rect = (30,50);
// println!("{}",area(rect));
// }
// fn area(dim:(u32,u32))->u32 {
// dim.0*dim.1
// }
//最终改进
#[derive(Debug)]
struct Rectangle {
width:u32,
length:u32,
}
fn main(){
let rect = Rectangle{
width:30,
length:50,
};
println!("{}",area(&rect));
println!("{:#?}",rect)
}
//计算长方形面积,借用Rectangle的实例
fn area(rect: &Rectangle) -> u32 {
rect.width * rect.length
}
枚举是一个很多语言都有的功能,不过不同语言中其功能各不相同。Rust 的枚举与 F#、OCaml 和 Haskell 这样的函数式编程语言中的 代数数据类型(algebraic data types)最为相似。
#![allow(unused)]
fn main() {
enum IpAddrKind {
V4,
V6,
}
}
//可以像这样创建 IpAddrKind 两个不同成员的实例:
#![allow(unused)]
fn main() {
enum IpAddrKind {
V4,
V6,
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
}
#![allow(unused)]
fn main() {
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
}
#![allow(unused)]
fn main() {
enum Option<T> {
Some(T),
None,
}
}
Option
枚举是如此有用以至于它甚至被包含在了 prelude 之中,你不需要将其显式引入作用域。另外,它的成员也是如此,可以不需要 Option::
前缀来直接使用 Some
和 None
。即便如此 Option
也仍是常规的枚举,Some(T)
和 None
仍是 Option
的成员。
接下来是 match
的分支。一个分支有两个部分:一个模式和一些代码。
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
//Coin::Penny是模式,1是代码,=>将其分开
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
编写一个函数,如果其中含有一个值,将其加一。如果其中没有值,函数应该返回 None 值,而不尝试执行任何操作
fn main() {
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
}
将 match
与枚举相结合在很多场景中都是有用的。
Rust 代码中很多这样的模式:match
一个枚举,绑定其中的值到一个变量,接着根据其值执行代码。
Rust 中的匹配是穷举式的(exhaustive):必须穷举到最后的可能性来使代码有效。
案例:
fn main() {
let dice_roll = 3;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
other => move_player(other),
}
fn add_fancy_hat() {
println!("你戴上了帽子,并没有走动")
}
fn remove_fancy_hat() {
println!("你摘下了帽子,并没有走动")
}
fn move_player(num_spaces: u8) {
println!("你走了{}步",num_spaces);
}
}
改变游戏规则:当你掷出的值不是 3 或 7 的时候,你必须再次掷出。
使用 _
来替代变量 other
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(),
}
fn add_fancy_hat() {
println!("你戴上了帽子,并没有走动")
}
fn remove_fancy_hat() {
println!("你摘下了帽子,并没有走动")
}
fn reroll() {
println!("重新")
}
}
改变游戏规则:如果你掷出 3 或 7 以外的值,你的回合将无事发生
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => (),
}
fn add_fancy_hat() {
println!("你戴上了帽子,并没有走动")
}
fn remove_fancy_hat() {
println!("你摘下了帽子,并没有走动")
}
}
处理只匹配一个模式的值而忽略其他模式的情况使用if let
if let
获取通过等号分隔的一个模式和一个表达式。它的工作方式与 match
相同,这里的表达式对应 match
而模式则对应第一个分支。
#![allow(unused)]
fn main() {
let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
println!("three");
}
}
可以在 if let
中包含一个 else
。else
块中的代码与 match
表达式中的 _
分支块中的代码相同
let mut count = 0;
match coin {
Coin::Quarter(state) => println!("State quarter from {:?}!", state),
_ => count += 1,
}
//等同于
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {:?}!", state);
} else {
count += 1;
}