Basics
Basics
编译和运行工具
# 单文件编译方法:rustc [filename]cargo创建新项目目录:
PS cargo new [dir_name]  Created binary (application) [dir_name] package目录会自动git仓库化,其中包含一个src文件夹和一个toml文件,src目录下有一个main.rs文件。使用cargo new --vcs=git似乎可以避免初始化为git仓库。
TOML文件使用的是专用的TOML格式,用来描述cargo的配置选项。初始有[package]和[dependencies]两个段,package段包含对当前包的信息描述,dependencies段包含这个包的依赖。Rust把包称为crates。
src目录用来包含所有的代码文件。
构建运行cargo项目(假设项目名为hello_cargo):
# 目标hello_cargo构建到target/debug/中
PS cargo build  
Compiling hello_cargo v0.1.0 (DIRECTORY\hello_cargo)    
Finished dev [unoptimized + debuginfo] target(s) in 0.56s    
# 一条龙构建运行
PS cargo run 
# 检查可编译性但不生成可执行文件
PS cargo check 
# 构建release文件到target/release/中
PS cargo build --releasehttps://learnku.com/rust/t/39451
VSCODE中安装一个RUST扩展即可,在设置中修改rust-client.channel为stable;设置editor.formatOnSave为true可以在保存时格式化代码(有rust-fmt的话)
cargo doc --openHello world
// use相当于import,如果没有这一句,下面使用io函数的时候就得写成std::io::[somefunc],这一点和C类似
// 语句statement应当以分号(;)结尾
use std::io;
// main函数的写法:
fn main(){    
    // 函数和宏:名字结尾带一个感叹号的是宏(Rust macro)    
    // 字符串:"something"表示了一个内容为something的字符串对象    
    println!("hello world");        
    // let关键字声明一个variable,其名字为foo,并被绑定到值5上,且不可修改(immutable)
    let foo1 = 5;    
    // 使用mut关键字声明一个可修改的variable    
    let mut foo2 = 3;        
    // String::new是一般意义上的构造函数,返回一个String类型的实例    
    // ::表明new是String的associated function(类似静态函数)    
    // String是可修改的UTF-8文本    
    let mut guess = String::new();        
    // io函数,返回std::io::Stdin实例
    io::stdin()        
        // 此处传参&表示传引用,mut表示是可变变量        
        // 返回io::Result描述结果,是enum变量,可以取值Ok或者Err        
        .read_line(&mut guess)
        // io::Result的expect方法,调用expect会在取值为Err时crash掉并打印传入的错误信息。
        // 这里不调用的话会被编译器警告        
        .expect("Readln failed");
    // 字符串格式化:println!使用格式化输出    
    println!("You guessed: {}", guess);
}引用外部crate时,需要修改toml文件中的[dependency]段。例如我们需要生成随机数,要用到rand crate,于是我们在toml文件中添加:
[dependencies]rand = "0.5.5"然后执行构建,cargo就会拉取兼容的crates。一旦拉取,cargo不会主动再更新,要把依赖更新到最新版本,需要执行更新指令:
cargo update然后可以使用随机数生成:
use rand::Rng;这里的Rng是一个trait,定义了随机数生成相关的方法。
rust允许将同一个变量名多次绑定到不同类型的值上去(类似python),称为shadow。
显式指定变量类型的方法为let [varname]: [typename] = [value];
可变性mutability
let x = 5; 
// 默认变量是不可变的。constant的规定更严格一些:const MAX_POINTS : u32 = 100;shadowing
重复使用 let 以同一变量名创建变量时,新变量覆盖旧变量(或者说该变量名被绑定到了新值上),这种覆盖称为 shadowing。(python 支持类似的机制)shadowing 会随着新变量的销毁(如程序退出新变量的 scope)而失效,此时该变量名将重新绑定到旧值。
let x = 5;
let x = x + 1;shadow 机制中,同一变量名可以绑定到不同的值类型。而通过将变量声明为 mut 以能够修改其值时,变量的类型是无法改变的。
ownership
- Each value in Rust has a variable that’s called its owner. 
- There can only be one owner at a time. 
- When the owner goes out of scope, the value will be dropped. 
超出作用域scope的变量拥有的内存会直接被drop掉。下面讨论各种情况:
// 5 是一个栈上的基本类型(见下讨论),y 会直接从 x 拷贝这个值,栈上有两个 5
let x = 5;
let y = x;
// 由于 String 的内容放在堆中,所以s2获得的是指向堆上内容的指针;
// 同时,为了保证退出作用域时不发生double-free,
// s1 对堆上内容的所有权失效,读 s1 会 CE。
// 这个过程相当于一次 move
let s1 = String::from("what");
let s2 = s1;
// 为了做深拷贝,必须调用 clone 方法
let s1 = String::from("what");
let s2 = s1.clone();有drop trait的变量无法附加copy trait。
函数传参和返回值时发生的事情类似。
但大多数时候我们希望变量把值传给函数后还能继续使用,如果让函数在返回值时再把所有权通过赋值退回来就太麻烦了。因此 rust 设计了 reference 机制。创建一个 reference 的过程称为 borrowing。
// 引用类型的参数
fn func_name(s: &String) -> usize {
    s.len()
}
fn main() {
    let s1 = String::from("what");
    let len = func_name(&s1); // 传引用
}为此带来的代价是,borrow来的值默认无法被修改(immutable)。为了能够修改参数,需要把parameter和argument声明为mut的。
fn main() {
    let mut s = String::from("hello");
    change(&mut s);
}
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}可变引用有一些奇怪的规则:
- 同一块数据在任一时刻最多只能有一个可变引用(持有者本身不算) - 如果一个可变引用被销毁了,那之后就可以创建新的可变引用 
 
- 同一块数据在任一时刻不能同时拥有可变和不可变的引用 
- 同一块数据可以拥有多个不可变引用 
基本类型
基本类型分为scalar和compound。这些类型都是在栈上分配的。
有copy trait的类型有所有scalar和tuple。
scalar包括:
- 整数值(i32, u32等,i表示有符号,u表示无符号,后面的数字表示位数;特别地 - isize和- usize的宽度取决于机器架构)- // 类型转换: let i: usize = 1; a = i as i32;
 
- 浮点(f64, f32,默认f64) 
- 布尔值( - true,- false)
- 字符(rust的字符是unicode编码,4字节宽) 
compound包括:
- tuple - 使用圆括号括起多个类型,长度不可变: 
- 可以被destructure,拆到多个变量,这个语法类似于python 
- 还可以用点号来索引tuple的元素 
- 这两种复值方法都不会破坏tuple本身,应该是相当于把值拷贝出来了 
- let tup: (i32, f64) = (50, 0.1); let (x, y) = tup; let tup: (i32, f64) = (50, 0.1); let x = tup.0; let y = tup.1;
 
- array - 使用方括号扩起多个相同类型的值,长度不可变;可变的是vector 
- 数组元素用方括号索引,运行时越界访问会抛出out of bounds异常 
- 循环遍历数组带来的索引合法性检查会影响性能,因此推荐的是使用iter()方法遍历。参见控制流部分。 
- let a = [1,2,3]; let a: [i32; 3] = [1,2,3]; // 声明数组类型 let a = [3; 5]; // 含有5个3的数组 let ele = a[0];
- 数组可以切片(slice),可参考字符串一节,其类型为 - &[EleType],切片语法同字符串
 
基本运算符
1 + 1
1 - 1
1 * 1
1 / 2
1 % 2
1 == 0
1 != 0函数
rust习惯使用snake case命名函数,即小写加下划线分隔
使用fn关键字可以定义一个函数。函数定义位置的先后不影响调用(和C不同)
严格来说parameter是函数参数,而argument是传入的实际值,不过实际常混用。rust的函数定义方式:
fn func_name(x: i32, y: i32) -> i32 {
	x + y
} // 返回x+yreturn语句依然可以使用,一般用于提前返回。
statement和expression
函数定义、变量赋值等都属于statement;
除了一般意义上的表达式外,{ }也是一个表达式。
let y = {
	let x = 3;
	x + 1
};控制流
if表达式(注意是一个表达式expression)实现一个条件分支,条件表达式不需要加括号,其返回值必须是bool变量(而不能是int之类)。
if num < 5 {
} else if {
    
} else {
}
// 条件表达式每个分支的返回值(如果返回了)必须是相同类型,否则CE
let number = if cond { 1 } else { 2 };loop表达式实现一个无限循环。
loop {
	instructions;
}使用break可以跳出循环,还可以顺带返回值:
loop {
	instructions;
	break 1; // return 1
}while结构也是有的:
while cond {
	instructions;
}for循环遍历区间:
// 1..4是(1..4)的略写(会被编译器提示省略圆括号……),(1..4) 是一个Range类型的实例,左闭右开。这里迭代变量num是不用预先声明的
for num in 1..4 {
}
// 反向遍历
for num in (1..4).rev() {
    
}for循环可以用来在一个集合结构上迭代,这种迭代比较快速而安全:
let a = [1,2,3];
for ele in a.iter() {
	// ...
}match语句类似C的switch,将表达式的结果和每个分支的pattern比较。(表达式的括号并不是必要的)
match expression {
	pattern => instructions,
	pattern => {
		instructions;
	}
}关于match的介绍详见 OOP - match,pattern & if let.
Math
随机数
use rand::Rng;
let number = rand::thread_rng().gen_range(1,101);
// 参数代表区间,左闭右开比较
use std::cmp::Ordering;
match num1.cmp(&num2) {
	Ordering::Less => [some instructions],
    Ordering::Greater => [],
    Ordering::Equal => [],
}Last updated
Was this helpful?