本文针对熟悉 Solidity 的开发者,介绍其常用语法并展示在 Rust 中的对应实现。
本文针对熟悉 Solidity 的开发者,介绍其常用语法并展示在 Rust 中的对应实现。让我们创建一个名为 tryrust 的 Anchor 项目,并逐步探索相关概念。
创建一个新 Anchor 项目:anchor init tryrust,并配置好开发环境。
Solidity 提供两种控制执行流程的方式:if-else 语句和三元运算符。以下是对比它们在 Solidity 和 Rust 中的实现。
在 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";
}
}
在 Rust(Solana 的 lib.rs 中添加以下函数):
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(())
}
注意:Rust 中 if 条件后的括号是可选的,age >= 18 不需额外括号。
测试代码(在 tests/tryrust.ts 中添加):
it("Age checker", async () => {
const tx = await program.methods.ageChecker(new anchor.BN(35)).rpc();
console.log("Your transaction signature", tx);
});
运行 anchor test 后的日志:
Transaction executed in slot 9:
Signature: 3uB77AcQpxCZPsFu4MaVdpwfGxDRXTmUEEdxDNGD1CnYodY9kcsRjR75pcSkXJs8qLn6gYCsEAPgqz6ovsYJkCBN
Status: Ok
Log Messages:
Program 7gHaAQrPS1TeCoohEu6F8RTC7bBauYVCfvriUdJrao8W invoke [1]
Program log: Instruction: AgeChecker
Program log: You are 18 years old or above
Program 7gHaAQrPS1TeCoohEu6F8RTC7bBauYVCfvriUdJrao8W consumed 340 of 200000 compute units
Program 7gHaAQrPS1TeCoohEu6F8RTC7bBauYVCfvriUdJrao8W success
Solidity 中的三元运算符示例:
function ageChecker(uint256 age) public pure returns (bool a) {
a = age % 2 == 0 ? true : false;
}
Rust 中没有直接的三元运算符,但可以用 if-else 表达式赋值给变量:
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 提供更强大的 match 控制流,类似于增强版的 switch。示例:
pub fn age_checker(ctx: Context<Initialize>, age: u64) -> Result<()> {
match age {
1 => msg!("The age is 1"),
2 | 3 => msg!("The age is either 2 or 3"),
4..=6 => msg!("The age is between 4 and 6"),
_ => msg!("The age is something else"),
}
Ok(())
}
match 支持单值匹配、多值匹配(用 | 分隔)、范围匹配(..= 表示包含边界)和通配符 _。
Solidity 中的 for 循环:
function loopOverSmth() public {
for (uint256 i = 0; i < 10; i++) {
}
}
Rust 中的等价写法:
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
for i in 0..10 {
}
Ok(())
}
Solidity 中步长为 2 的循环:
function loopOverSmth() public {
for (uint256 i = 0; i < 10; i += 2) {
}
}
Rust 中使用 step_by:
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
for i in (0..10).step_by(2) {
msg!("{}", i);
}
Ok(())
}
运行测试后的日志:
Transaction executed in slot 3:
Signature: 3DVe2LzukpWnu2A3HkATFMET7BVd8pA7JTumJ51u7AT615BH5vobbdBsWS4MZfwHo5txk3zFC1mLnDLbMDWGUET6
Status: Ok
Log Messages:
Program log: 0
Program log: 2
Program log: 4
Program log: 6
Program log: 8
Program consumed 2272 of 200000 compute units
Rust 的数组支持与 Solidity 不同。Solidity 内置固定和动态数组,而 Rust 原生支持固定数组,动态长度需使用 Vec。
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let my_array: [u32; 5] = [10, 20, 30, 40, 50];
let first_element = my_array[0];
let third_element = my_array[2];
let mut mutable_array: [u32; 3] = [100, 200, 300];
mutable_array[1] = 250;
Ok(())
}
使用 Vec 实现动态数组:
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let mut dynamic_array: Vec<u32> = Vec::new();
dynamic_array.push(10);
dynamic_array.push(20);
dynamic_array.push(30);
let first_element = dynamic_array[0];
let third_element = dynamic_array[2];
msg!("Third element = {}", third_element);
Ok(())
}
注意:dynamic_array 需声明为 mut,以支持修改操作。
运行日志:
Transaction executed in slot 3:
Signature: JF5UwTt1gdD3Re2TmNXMRiG8WV6Khc7NzUwS5c6dGjR2jTjYSvbf3KenwQKaFtK3vu5aps1VSrhEJQBBPGYmPri
Status: Ok
Log Messages:
Program log: Third element = 30
Program consumed 769 of 200000 compute units
Solidity 有内置的 mapping,而 Rust 使用标准库中的 HashMap。注意,此处 HashMap 在内存中操作,未涉及 Solana 链上存储(后续教程会讨论存储)。
示例:
use anchor_lang::prelude::*;
use std::collections::HashMap;
declare_id!("7gHaAQrPS1TeCoohEu6F8RTC7bBauYVCfvriUdJrao8W");
#[program]
pub mod tryrust {
use super::*;
pub fn initialize(ctx: Context<Initialize>, key: String, value: String) -> Result<()> {
let mut my_map = HashMap::new();
my_map.insert(key.to_string(), value.to_string());
msg!("My name is {}", my_map[&key]);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}
更新测试:
it("Is initialized!", async () => {
const tx = await program.methods.initialize("name", "Bob").rpc();
console.log("Your transaction signature", tx);
});
运行日志:
Transaction executed in slot 3:
Signature: 37VrfZkJr3EeWUqHmnjGW4Lsdz5xcuSDKqTtqoSg2WiNPQmL5FnCTydxcGs91XZBjBjUiPhJnzHe5zj73LA7opoG
Status: Ok
Log Messages:
Program log: My name is Bob
Program consumed 2420 of 200000 compute units
Solidity 和 Rust 都支持结构体定义自定义数据结构。
Solidity 示例:
contract SolidityStructs {
struct Person {
string my_name;
uint256 my_age;
}
Person person1;
function initPerson1(string memory name, uint256 age) public {
person1.my_name = name;
person1.my_age = age;
}
}
Rust 等价实现:
pub fn initialize(_ctx: Context<Initialize>, name: String, age: u64) -> Result<()> {
struct Person {
my_name: String,
my_age: u64,
}
let mut person1 = Person { my_name: name, my_age: age };
msg!("{} is {} years old", person1.my_name, person1.my_age);
person1.my_name = "Bob".to_string();
person1.my_age = 18;
msg!("{} is {} years old", person1.my_name, person1.my_age);
Ok(())
}
测试:
it("Is initialized!", async () => {
const tx = await program.methods.initialize("Alice", new anchor.BN(20)).rpc();
console.log("Your transaction signature", tx);
});
日志:
Transaction executed in slot 3:
Signature: 3vSTwusBVN2fJcgqBAsUT44s5GnmcmwcAmhS1jq36b3oeHhZf1xuuZDRCXjjfa4bNuWHCmida3sKhCNYJwLv7r9v
Status: Ok
Log Messages:
Program log: Alice is 20 years old
Program log: Bob is 18 years old
Program consumed 2362 of 200000 compute units
注意:Solidity 示例将结构体存储在链上,而 Rust 示例仅在函数内操作,未涉及存储。
Rust 使用 const 定义常量,可在 #[program] 外声明:
use anchor_lang::prelude::*;
declare_id!("7gHaAQrPS1TeCoohEu6F8RTC7bBauYVCfvriUdJrao8W");
const MEANING_OF_LIFE_AND_EXISTENCE: u64 = 42;
#[program]
pub mod tryrust {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
msg!("Answer to the ultimate question: {}", MEANING_OF_LIFE_AND_EXISTENCE);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize {}
Rust 中无符号整数通常为 u64,但数组或 Vec 的长度是 usize 类型,需进行类型转换:
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(); // usize
let another_var: u64 = 5; // u64
let len_plus_another_var = len as u64 + another_var;
msg!("The result is {}", len_plus_another_var);
Ok(())
}
Rust 没有 try-catch。错误处理通过返回 Result(如 Solana 的 Ok 和 Err)或使用 panic! 处理不可恢复错误。
【笔记配套代码】 https://github.com/0xE1337/rareskills_evm_to_solana 【参考资料】 https://learnblockchain.cn/column/119 https://www.rareskills.io/solana-tutorial
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!