Solidity 开发者必知的 Rust 语法基础

  • 0xE
  • 发布于 2025-03-22 09:25
  • 阅读 427

本文针对熟悉 Solidity 的开发者,介绍其常用语法并展示在 Rust 中的对应实现。

本文针对熟悉 Solidity 的开发者,介绍其常用语法并展示在 Rust 中的对应实现。让我们创建一个名为 tryrust 的 Anchor 项目,并逐步探索相关概念。

创建一个新 Anchor 项目:anchor init tryrust,并配置好开发环境。


条件语句

Solidity 提供两种控制执行流程的方式:if-else 语句和三元运算符。以下是对比它们在 Solidity 和 Rust 中的实现。

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";
    }
}

在 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(())
}

注意

  • if-else 表达式以分号结尾,因为它是赋值语句。
  • 表达式内部不加分号,因为它是返回值。

Match 控制流

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 支持单值匹配、多值匹配(用 | 分隔)、范围匹配(..= 表示包含边界)和通配符 _。


For 循环

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

数组与 Vector

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(())
}

动态数组(Vector)

使用 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

映射(HashMap)

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 常量

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 {}

usize 类型与类型转换

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(())
}

Try-Catch 的缺失

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

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
0xE
0xE
0x59f6...a17e
17年进入币圈,Web3 开发者。刨根问底探链上真相,品味坎坷悟 Web3 人生。