RUST入门
Hello World
mkdir -p projects/hello_world/
cd projects/hello_world
main.rs文件
fn main() {
println!("hello, world!");
}
编译和运行
rustc main.rs
./main
格式化:rustfmt
Cargo
Cargo 是 Rust 的构建系统和包管理器。
hello
构建如下:
-
cargo new hello_world --bin
--bin
为默认参数,表示构建二进制程序。可选--lib
表示构建库。nsfoxer@nsfoxer-pc ~/temp> cargo new hello_world --bin Created binary (application) `hello_world` package nsfoxer@nsfoxer-pc ~/temp> tree hello_world hello_world ├── Cargo.toml └── src └── main.rs 1 directory, 2 files
cargo会同时构建git项目。使用
--vcs none
禁用git存储。 -
编译:
cargo build
将获取所有依赖项。
nsfoxer@nsfoxer-pc ~/t/hello_world> tree . . ├── Cargo.lock ├── Cargo.toml ├── src │ └── main.rs └── target ├── CACHEDIR.TAG └── debug ├── build ├── deps │ ├── hello_world-efb3cc30327658a6 │ └── hello_world-efb3cc30327658a6.d ├── examples ├── hello_world ├── hello_world.d └── incremental └── hello_world-2bu9x4t5793bs ├── s-g1qy781nco-1nkogqh-2hbtkajf7iavr │ ├── 1afbzx1y0x1dbpio.o │ ├── 1ko7w99wy4kz2chh.o │ ├── 28cobucdo6rhinaa.o │ ├── 2goevba305ege8v6.o │ ├── 4d0psjpawt67vlja.o │ ├── 5byta3abs29cbrhj.o │ ├── 5dygtqqmhu41ip90.o │ ├── dep-graph.bin │ ├── gfq0hw1bwbsq84d.o │ ├── query-cache.bin │ └── work-products.bin └── s-g1qy781nco-1nkogqh.lock 9 directories, 20 files
Cargo.lock
包含依赖相关信息。使用--release
开启编译优化。Caogo.toml
包含项目的各种元信息。 -
运行:
./target/debug.hello_world
可以使用cargo run
进行编译和运行。
可以使用cargo check
进行代码检查。
目标
cargo允许Rust项目声明其各种依赖,并保证始终可重复构建。
- 引入两个含有各种信息的元数据文件;
- 获取并构建项目的依赖项;
- 调用
rustc
或其他工具进行构建; - 使用 Rust 项目的约定(规范/风格)。
依赖
crates.io是 Rust 社区的中央存储库,用作发现和下载包的位置。cargo
默认配置为,使用它来查找请求的包。
添加依赖
[dependencies]
time = "0.1.12"
regex = "0.1.41"
cargo build
将会获取依赖。如果regex
在crates.io上更新了,在我们选择cargo update
之前,我们仍会使用相同的版本进行构建.
文件布局
.
├── Cargo.lock
├── Cargo.toml
├── benches # 基准测试
│ └── large-input.rs
├── examples # 示例
│ └── simple.rs
├── src
│ ├── bin # 其他可执行文件
│ │ └── another_executable.rs
│ ├── lib.rs # 库文件
│ └── main.rs # 可执行文件
└── tests # 集成测试
└── some-integration-tests.rs
Carogo.toml && Carogo.lock
Cargo.toml
是从广义上描述你的依赖,并由你编写.Cargo.lock
包含有关您的依赖项的确切信息。它由 Cargo 维护,不应手动编辑.- 构建其他项目要依赖的库,请将
Cargo.lock
放置在你的.gitignore
- 构建可执行文件,如命令行工具或应用程序,请检查
Cargo.lock
位于git
管理下。
[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand.git", rev = "9f35b8e" }
如上,依赖于github上的项目,当未指定其他信息时默认依赖github上的最新版本。例子中rev
指定github的版本。
Cargo.lock
会记录项目第一次构建时依赖版本,此时不需要进行手动添加加具体版本。
当我们准备选择,更新库的版本时,Cargo 会自动重新计算依赖关系,并为我们更新内容:
$ cargo update # updates all dependencies
$ cargo update -p rand # updates just “rand”
测试
cargo test
:会查找src
和tests
的测试
通用编程
基本类型
-
整型
有符号 无符号 长度(字节) i8 u8 1 i16 u16 2 i32 u32 4 i64 u64 8 isize usize arch 其中,isize表示与机器架构一致。
进制 示例 decimal 98_222 hex 0xff Octal 0o77 Binary 0b1110_0000 Byte(u8 only) b'A' 注:此处与c不同,byte属于整形,占位1字节。而char占位4字节。
-
浮点数
类型:
f32
、f64
现代CPU在32位和64位浮点数运算效率几乎一致。Rust默认使用64位浮点数。
-
bool
值:
true
和false
,占位1字节。 -
字符
char
:占位4字节,代表一个Unicode标量。
复合类型
-
元组
长度固定,不能改变长度大小。但内部类可以不一致。
let tup : {i32, f64, u8} = {500, 6.4, 1}; // 此处类型注解可以推断(?) // 类似ES6,模式匹配 解构 // 等价于 x = tup.0 // y = tup.1 // z = tup.2 let {x, y, z} = tup;
-
数组
与c不同,rust的数组长度固定。内部类型也需要一致。
let a:[i32; 5] = {1, 2, 3, 4, 5} // 创建相同元素的数组 // 以下写法等价于 let a = [3, 3, 3, 3, 3] let a = [3; 5] // 数组访问 let first = a[0];
当访问数组越界时,程序会崩溃。rust内部包含了运行检查。
函数
rust使用snake case
进行命名,所有字母小写,以下划线分隔单词。
被调用函数定义在调用函数范围内即可。
fn main() {
another_function(5, 6);
}
fn another_function(x : i32, y : i32) {
println!("Another Funcion!");
}
函数参数必须显式声明,编译器由此不需要其他代码对参数类型进行推断。
rust是基于表达式(表达式是会计算并返回值;语句是只执行操作,但不返回值)
let x = 5; // 语句
let x = y = 6; // error
// 调用函数,调用宏,{}都是表达式
let y = {
let x = 3;
x + 1
} // y == 4
rust的函数返回值,等价于最后一个表达式的返回值,可以隐式返回:
fn five() -> i32 {
5
}
控制流
-
if表达式
条件表达式必须产生一个bool值。
let number = 5; if number < 5 { // .... } else { // ..... } if number { // error, rust不会将非bool类型转换为bool类型 }
if是一个表达式,可以直接生成值。但每条分支返回值类型应该相同。
let condition = true; let number = if condition { 5 } else if somthing{ 6 } else { "seven" // error, 返回值类型不同 };
-
loop
可以在将
break
后的值,作为返回值let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 10; } };
-
while
-
for
使用for进行迭代
let a = [10;9]; for element in a.iter() { println!("{}", elment); }
所有权
基本规则
- 每个值都有一个对应的变量作为其所有者
- 在同一时间内,有且只有一个所有者
- 当所有者离开其作用域时,其持有的值就会被释放
变量使用的内存在变量本身离开作用域后自动进行释放,(调用drop()
的特殊函数)。为避免二次内存释放,rust在变量被移动时(复制到其他变量),将把此变量废弃,不再使用。rust不会自动深度拷贝,任何自动的赋值操作都可以被认为是高效的。
深度拷贝使用clone()方法。
拥有Copy的trait的变量类型赋值不会被废弃:所有整型
bool
char
浮点型
内部字段全可Copy的元组
引用和借用
当变量赋值时,所有权就转移。当希望调用函数保留参数的所有权,必须将参数返回。
fn main() {
let s1 = String::from("ADMIN");
let (s2, len) = calculate_length(s1); // s2想保留s1的所有权,必须将s1返回,否则函数参数s获得s1的所有权,在函数结束后,s离开作用范围,内存被释放。
println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s:String) -> (String, usize) {
let length = s.len();
return (s, length);
}
引用
允许在不获得所有权下,使用值。
fn main() {
let s1 = String::from("ADMIN");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s:&String) -> usize {
s.len() // s不会销毁s1的数据,因为s没有所有权
}
引用默认是不可变的,不能修改值。
可变引用
添加mut
来接收可变引用。
fn main() {
let mut s1 = String::from("ADMIN");
add_string(&mut s1);
println!("The length of '{}' is {}.", s1, s1);
}
fn add_string(s : &mut String) {
s.push_str(" World!");
}
可变引用的限制:特定作用域的特定数据,一次只能声明一个可变引用。可以多次普通引用,但不能在有可变引用的情况下,再次普通引用。(类似于读写锁,可以同时有多个读,但是只能有一个写)
let mut s = "admin";
let r1 = &mut s;
let r2 = &mut s; // 错误,多次引用可变
悬垂引用:悬垂指针,指针指向某一内存,但此内存在其他地方被释放。rust使用生命周期防止悬垂引用。
切片
字符串切片
fn main() {
let s1 = String::from("ADMIN WORLD JKL");
println!("{}", frist_word(&s1));
}
fn frist_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
return &s[..];
}
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
结构体
// 添加debug来使用 {:?} debug输出
#[derive(Debug)]
struct User {
username: String,
email: String,
active: bool,
sign_in_count: u64,
}
// 结构体的方法,可以有多个impl
impl User {
fn build_user(email:String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
}
impl User {
// 方法,第一个参数永远是self,一般为引用类型,直接self一般用于强转类型
fn add_sign(& mut self, number: u64) {
self.sign_in_count += number;
}
}
fn main() {
// 定义user1,字符串来自String方法,否则错误。
let user1 = User::build_user(String::from("ex.@email.org"), String::from("example"));
let mut user2 = User {
email: String::from("test@q.org"),
username: String::from("test2"),
..user1 // 此处为简写,类似JavaScript中的ES6语法: 同名直接赋值
};
user2.add_sign(32);
println!("user1: {:?}", user1);
println!("user2: {:?}", user2);
// 结构元祖
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
// black 和 origin不能相互赋值,因为类型不同
let mut black = Color(0, 0, 0);
let mut origin = Point(0, 0, 0);
}
枚举enum和模式匹配match
enum
#[derive(Debug)]
// 枚举可以和相关数据直接关联
enum Message {
Quit, // 没有关联任何数据
Move{x: i32, y: i32}, // 包含匿名结构体
Write(String), // 包含String
ChangeColor(i32, i32, i32), // 包含三个i32
}
fn main() {
let msg = Message::ChangeColor(0, 0, 0);
println!("msg {:?}", msg);
}
Option
rust处理空值使用枚举Option处理,Option也会被预导入,定义为:
enum Option<T> {
Some(T),
None
}
fn main() {
let x: i8 = 8;
let y: Option<i8> = Some(9);
// error! x 和 y 类型不同
let sum = x + y;
}
match
#[derive(Debug)]
enum A {
Alibaba,
Tencent,
Baidu,
}
enum B {
US,
CN,
Coo(A), // 将Coo绑定一个A枚举
}
fn main() {
let t = B::Coo(A::Alibaba);
let value = match t {
B::US => 1000,
B::CN => 2000,
B::Coo(coo) => {
println!("coo: {:?}", coo);
20
},
};
println!("value: {:?}", value);
// println!("t: {:?}", t);
}
模式匹配必须匹配完所有类型,可以使用通配符_
代替其他所有值。
可以使用if let
来简单控制。
包管理
模块系统:
- 包
package
- 单元包
crate
- 模块
module
- 路径
path
模块定义
mod front_of _house {
mod hosting {
fn add() {}
}
}
路径
模块引用 绝对路径
和相对路径
pub fn eat() {
// 绝对路径
crate::front_of_house::hosting::add();
// 相对路径
front_of_house::hosting::add();
}
-
使用pub暴露路径
-
super
类似..
,上一级路径 -
使用
use
导入路径 -
as
重命名 -
pub use
重导出
use std::fmt::Result;
use std::io::Result as IOResult;
use std::*;