Rust learning 01
Solana 是目前最快的区块链网络,而以太坊已经逐渐走下坡路,而且考虑到 Solana 优秀的设计,以及 Rust 的执行速度的加持,在可以预见的相当一段时间内,它可能都是最流行的区块链网络,所以学习合约开发的话,我打算以 Solana 平台开始。
同时,Rust 是目前集抽象能力与性能为一体的最流行的编程语言,有了 Solana 的招牌效应,在未来即使有新的区块链出现,也很有可能会继续采用 Rust ,所以在进行合约开发钱最好先学习一下 Rust 的常规用法。
安装
按照官网使用 rustup 把所有组件安装好,使用默认安装方式,它一次性就把所用到的组件安装到了 $HOME/.cargo/bin
目录下,下面这三个是最主要的
- rustup: 管理 rust 相关的工具链,比如升级等等
- rustc: 编译器,相当于 gcc
- cargo: 包管理器,相当于 npm 或者 poetry
Cargo
使用 rustc 编译单个文件还行,但如果文件多了,甚至还有一大堆第三方依赖的话,编译的时候就得传很多参数。所以 cargo 就是用来解决这个问题的,它定义了 rust 项目的一个标准目录结构,他可以完成管理依赖、编译、发布等工作。实际项目开发中基本都会用 cargo 来管理项目。
cargo 初始化项目时,需要选择这个项目的最终产物是一个可以单独运行的文件,还是一个供其他项目引入的依赖包,前者是 cargo new [--bin]
,后者是 cargo new --lib
,学习阶段线使用 --bin
的方式即可。
使用 cargo new hello-rust
得到了一个初始目录,其结构如下,和大多数比较流行的语言的目录差不多,一个源码目录,一个包的描述文件,安装依赖后生成一个 lock 文件。
src/ # 源码目录
|- main.rs
Cargo.lock
Cargo.toml # 包的信息
代码示例
官方提供了一个简单的猜数字游戏的代码,如下所示
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1..=100);
loop {
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess = match guess.trim().parse::<u32>() {
Ok(num) => num,
Err(_) => continue,
};
println!("You guessed: {guess}");
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
对于每一个 Rust 程序,都会默认包含一些 Rust 的标准库里的东西在里面,这叫 prelude (预导入),如果你想要的东西没有被 prelude 的话,就需要显式地使用 use
将他们导入到程序中,use std::io
便引入了很多功能,包括读取用户输入的功能。
在上面的例子中
-
println!
是一个宏(带了感叹号的都是宏) -
let secret_number = rand::thread_rng().gen_range(1..=100);
表示调用rand
crate 的thread_rng
关联函数,创建一个rand::Rng
实例,然后再调用这个实例的gen_range
方法生成一个 1 到 100 之间(闭区间)的随机整数。 -
let mut guess = String::new();
定义一个String
类型的变量mut
意味着这个变量可修改,如果是常量的话就不要加mut
::
的表示new
是String
类型的一个关联函数,关联函数就是某个类型所实现的函数,很多类型都有new
函数,它通常表示用于创建该类型的一个实例
-
io::stdin().read_line(&mut guess)
中表示在 callstd::io
模块上的stdin
函数- 如果没有手动
use
这个模块,也可以写std::io::stdin()
,有点像 python 的from aaa import bbb
和import aaa.bbb
的意思 - 这个函数返回了一个
std::io::Stdin
实例
- 如果没有手动
-
.read_line(&mut guess)
调用了std::io::Stdin
实例的read_line
方法来获取用户的输入read_line
就是把用户输入的任何内容追加到一个字符串上,所以它的参数是一个 string 类型&
表示这个参数是一个引用,它可以使得你的程序在多个地方访问相同的数据,而不是把一个数据在内存中多次拷贝- 像变量一样,引用默认也是不可修改的,所以这里要用
&mut guess
而不是&guess
-
read_line
返回了一个std::result::Result
类型的值,它是一个枚举enumeration
,它可能有很多状态,它的每个状态被叫做variant
(有点像薛定谔的猫)。Result
的变体是Ok
和Err
,而Result
类型的值本身也像其它类型一样,有一些方法,其中一个方法就说expect
。当这个Result
是一个Err
时,expect
方法就会让程序崩溃并且显示你传给expect
的参数。而如果Result
是Ok
,那么expect
就会返回Ok
所 holding 的返回值给你拿来用。 -
这段代码和上面的
match
类似,guess.trim().parse::<u32>()
返回Result
类型,match
和后面的花括号则是进行模式匹配然后选择是返回转换后的数值还是使用continue
进入下一轮循环。let guess = match guess.trim().parse::<u32>() { Ok(num) => num, Err(_) => continue, };
-
loop
是一个无限循环,两个match
代码块中的continue
和break
的逻辑都是针对这个循环的。