面向 Solidity 开发人员的基本 Rust 知识
更新日期:Feb 29

本教程介绍了 Solidity 中最常用的语法,并演示了 Rust 中的类似语法。
如果你想要了解 Rust vs Solidity 的区别,请参考链接的教程。本教程假定你已经了解 Solidity,如果你对 Solidity 不熟悉,请参阅我们提供的免费 Solidity 教程。
创建一个新的 Solana Anchor 项目,名为 tryrust ,并设置环境。
条件语句
在 Solidity 中有两种开发人员可以根据特定条件控制执行流程的方式:
- If-Else 语句
- 三元运算符
现在让我们看看在 Solidity 中的如何表示上述内容,以及它们在 Solana 中的语法。
If-Else 语句
在 Solidity 中:
function ageChecker(uint256 age)
public pure returns (string memory) {
if (age >= 18) {
return "You are 18 years old or above";
} else {
return "You are below 18 years old";
}
}
在 Solana 项目中,在 lib.rs 中添加一个名为 age_checker 的新函数:
#![allow(unused)] fn main() { pub fn age_checker(ctx: Context<Initialize>, age: u64) -> Result<()> { if age >= 18 { msg!("You are 18 years old or above"); } else { msg!("You are below 18 years old"); } Ok(()) } }
请注意,条件 age >= 18 不需要括号 — if 语句中的括号是可选的。
为了测试,在 ./tests/tryrust.ts 中添加另一个 it 代码块:
it("Age checker", async () => {
// Add your test here.
const tx = await program.methods.ageChecker(new anchor.BN(35)).rpc();
console.log("Your transaction signature", tx);
});
运行测试后,我们应该看到以下日志:

三元运算符
在 Solidity 中将 if-else 语句赋给变量:
function ageChecker(uint256 age) public pure returns (bool a) {
a = age % 2 == 0 ? true : false;
}
在 Solana 中,我们基本上只是将 if-else 语句赋给一个变量。下面的 Solana 程序与上面的相同:
#![allow(unused)] fn main() { pub fn age_checker(ctx: Context<Initialize>, age: u64) -> Result<()> { let result = if age >= 18 {"You are 18 years old or above"} else { "You are below 18 years old" }; msg!("{:?}", result); Ok(()) } }
请注意,在 Rust 中的三元运算符示例中,if/else 块以分号结尾,因为这将被赋给一个变量。
还要注意,内部值结尾没有分号,因为它作为返回值返回给变量,类似于你在 Ok(()) 后面不加分号,因为它是一个表达式而不是语句。
程序在 age 为偶数时输出 true,否则输出 false:

Rust 还有一个更强大的控制流运算符叫做 match。让我们看一个 match 示例:
#![allow(unused)] fn main() { pub fn age_checker(ctx: Context<Initialize>, age: u64) -> Result<()> { match age { 1 => { // Code block executed if age equals 1 msg!("The age is 1"); }, 2 | 3 => { // Code block executed if age equals 2 or 3 msg!("The age is either 2 or 3"); }, 4..=6 => { // Code block executed if age is in the // range 4 to 6 (inclusive) msg!("The age is between 4 and 6"); }, _ => { // Code block executed for any other age msg!("The age is something else"); } } Ok(()) } }
For 循环
正如我们所知,for 循环允许循环遍历范围、集合和其他可迭代对象,Solidity 中的写法如下:
function loopOverSmth() public {
for (uint256 i=0; i < 10; i++) {
// do something...
}
}
这是 Solana(Rust)中的等效写法:
#![allow(unused)] fn main() { pub fn initialize(ctx: Context<Initialize>) -> Result<()> { for i in 0..10 { // do something... } Ok(()) } }
是的,就是这么简单,但是如何使用自定义步长迭代范围呢?以下是 Solidity 中预期的行为:
function loopOverSmth() public {
for (uint256 i=0; i < 10; i+=2) {
// do something...
// Increment i by 2
}
}
这是在 Solana 中使用 step_by 的等效写法:
#![allow(unused)] fn main() { pub fn initialize(ctx: Context<Initialize>) -> Result<()> { for i in (0..10).step_by(2) { // do something... msg!("{}", i); } Ok(()) } }
运行测试后,我们应该看到以下日志:

数组和 Vector
Rust 在数组支持方面与 Solidity 不同。虽然 Solidity 对固定数组和动态数组都有原生支持,但 Rust 只对固定数组有内置支持。如果你想要一个动态长度的列表,请使用 Vector。
现在,让我们看一些示例,演示如何声明和初始化固定数组和动态数组。
固定数组
#![allow(unused)] fn main() { pub fn initialize(ctx: Context<Initialize>) -> Result<()> { // Declare an array of u32 with a fixed size of 5 let my_array: [u32; 5] = [10, 20, 30, 40, 50]; // Accessing elements of the array let first_element = my_array[0]; let third_element = my_array[2]; // Declare a mutable array of u32 with a fixed size of 3 let mut mutable_array: [u32; 3] = [100, 200, 300]; // Change the second element from 200 to 250 mutable_array[1] = 250; // Rest of your program's logic Ok(()) } }
动态数组
在 Solana 中模拟动态数组的方法涉及使用 Rust 标准库中的 Vec(向量)。以下是一个示例:
#![allow(unused)] fn main() { pub fn initialize(ctx: Context<Initialize>) -> Result<()> { // Declare a dynamic array-like structure using Vec let mut dynamic_array: Vec<u32> = Vec::new(); // Add elements to the dynamic array dynamic_array.push(10); dynamic_array.push(20); dynamic_array.push(30); // Accessing elements of the dynamic array let first_element = dynamic_array[0]; let third_element = dynamic_array[2]; // Rest of your program's logic msg!("Third element = {}", third_element); Ok(()) } }
dynamic_array 变量必须声明为可变的(mut),以允许变量可以变化(push、pop、在索引处覆盖等)。
运行测试后,程序应该记录如下:

映射
与 Solidity 不同,Solana 缺乏内置的映射数据结构。但是,我们可以通过使用 Rust 标准库中的 HashMap 类型来在 Solana 中复制键值映射功能。与 EVM 链不同,我们在这里演示的映射是在内存中,而不是在存储中。EVM 链没有内存中的哈希映射。 我们将在稍后的教程中演示 Solana 存储中的映射。
让我们看看如何使用 HashMap 在 Solana 中创建映射。将提供的代码片段复制并粘贴到 lib.rs 文件中,并替换程序 ID:
#![allow(unused)] fn main() { use anchor_lang::prelude::*; declare_id!("53hgft52DHUKMPHGu1kusuwxFGk2T8qngwSw2SyGRNrX"); #[program] pub mod tryrust { use super::*; // Import HashMap library use std::collections::HashMap; pub fn initialize(ctx: Context<Initialize>, key: String, value: String) -> Result<()> { // Initialize the mapping let mut my_map = HashMap::new(); // Add a key-value pair to the mapping my_map.insert(key.to_string(), value.to_string()); // Log the value corresponding to a key from the mapping msg!("My name is {}", my_map[&key]); Ok(()) } } }
my_map 变量也被声明为可变的,以便我们可以编辑它(即添加/删除键值对)。还注意到我们是如何导入 HashMap 库的吗?
由于 initialize 函数接收两个参数,测试也需要更新:
it("Is initialized!", async () => {
// Add your test here.
const tx = await program.methods.initialize("name", "Bob").rpc();
console.log("Your transaction signature", tx);
});
运行测试时,我们看到以下日志:

结构体
在 Solidity 和 Solana 中,结构体用于定义可以容纳多个字段的自定义数据结构。让我们看一个在 Solidity 和 Solana 中的结构体示例。
在 Solidity 中:
contract SolidityStructs {
// Defining a struct in Solidity
struct Person {
string my_name;
uint256 my_age;
}
// Creating an instance of the struct
Person person1;
function initPerson1(string memory name, uint256 age) public {
// Accessing and modifying struct fields
person1.my_name = name;
person1.my_age = age;
}
}
在 Solana 中的一一对应:
#![allow(unused)] fn main() { pub fn initialize(_ctx: Context<Initialize>, name: String, age: u64) -> Result<()> { // Defining a struct in Solana struct Person { my_name: String, my_age: u64, } // Creating an instance of the struct let mut person1: Person = Person { my_name: name, my_age: age, }; msg!("{} is {} years old", person1.my_name, person1.my_age); // Accessing and modifying struct fields person1.my_name = "Bob".to_string(); person1.my_age = 18; msg!("{} is {} years old", person1.my_name, person1.my_age); Ok(()) } }
练习: 更新测试文件,将两个参数 Alice 和 20 传递给 initialize 函数并运行测试,你应该得到以下日志:

在提供的代码片段中,Solidity 将结构体的实例存储在存储中,而 Solana 实现中,一切都发生在 initialize 函数中,没有任何东西存储在链上。存储将在以后的教程中讨论。
Rust 中的常量
在 Rust 中声明常量变量很简单。不使用 let 关键字,而是使用 const 关键字。这些可以在 #[program] 块之外声明。
#![allow(unused)] fn main() { use anchor_lang::prelude::*; declare_id!("EiR8gcMCX11tYMRfoZ2vyheZsZ2NvdUTvYrRAUvTtYnL"); // *** CONSTANT DECLARED HERE *** const MEANING_OF_LIFE_AND_EXISTENCE: u64 = 42; #[program] pub mod tryrust { use super::*; pub fn initialize(ctx: Context<Initialize>) -> Result<()> { msg!(&format!("Answer to the ultimate question: {}", MEANING_OF_LIFE_AND_EXISTENCE)); // new line here Ok(()) } } #[derive(Accounts)] pub struct Initialize {} }
usize 类型和类型转换
在 Solana 中,我们大多数时候可以假设无符号整数是 u64 类型,但在测量列表长度时有一个例外:它将是 usize 类型。你需要像下面的 Rust 代码演示的那样对变量进行转换:
#![allow(unused)] fn main() { use anchor_lang::prelude::*; declare_id!("EiR8gcMCX11tYMRfoZ2vyheZsZ2NvdUTvYrRAUvTtYnL"); #[program] pub mod usize_example { use super::*; pub fn initialize(ctx: Context<Initialize>) -> Result<()> { let mut dynamic_array: Vec<u32> = Vec::from([1,2,3,4,5,6]); let len = dynamic_array.len(); // this has type usize let another_var: u64 = 5; // this has type u64 let len_plus_another_var = len as u64 + another_var; msg!("The result is {}", len_plus_another_var); Ok(()) } } #[derive(Accounts)] pub struct Initialize {} }
Try Catch
Rust 没有 try catch。失败预期返回错误(就像我们在 Solana 的教程中所做的那样)或对于不可恢复的错误会 panic。
练习: 编写一个接受 u64 向量、循环遍历它并将所有偶数写入到另一个向量,然后打印新向量的 Solana / Rust 程序。
通过 RareSkills 了解更多
本教程是我们免费的 Solana 课程 的一部分。