本文介绍了如何使用Rust编写和部署Solana区块链上的“Hello, world!”程序,重点介绍了Rust的基础知识、Solana程序的结构以及关键的solana_program crate。文章还解释了模块、crate、路径的概念,以及如何在Solana程序中使用entrypoint!宏和process_instruction函数来处理指令,是Solana入门级别的教程。

有没有想过在 Solana 区块链上创建一个程序需要做些什么?本指南将引导你使用 Rust 编写和部署一个“Hello, world!”程序的基础知识,而不依赖于任何框架。这会给你更多的控制权,但也意味着我们需要自己完成一些基本的工作。
在我们开始编写程序之前,让我们回顾一些关键的 Rust 概念。如果你想深入了解,Rust 编程语言 是一份很棒的资源。
Rust 有一个组织代码的系统,称为“模块系统”:
Cargo.toml 文件)。在本指南中,我们将主要关注 crates 和 模块。
Crates 包含模块,这些模块可以在不同的项目中共享。 要使用模块中的某些内容,你需要知道它的“路径”,就像在计算机上查找文件一样。
将你的 crate 想象成一棵树的根,模块是分支。 每个分支可以有较小的分支(子模块)或单个项目(如函数或数据结构)。 特定项目的路径就像从 crate 一直追溯到该项目的步骤,每个步骤用 :: 分隔。
例如,solana_program crate 有一个名为 account_info 的模块,该模块又包含一个名为 AccountInfo 的东西。 因此,AccountInfo 的完整路径将是:solana_program::account_info::AccountInfo。
通常,每次你想使用 AccountInfo 时都必须写出整个路径。 但是 Rust 有一个方便的关键字叫做 use。 use 关键字允许你将项目“引入”到你当前的作用域中,这意味着你可以使用它,而无需每次都输入其完整路径。 你经常会在 Rust 文件的顶部看到一堆 use 语句:
use solana_program::account_info::AccountInfo;
Rust 中的函数使用 fn 关键字定义,后跟函数名称和一组括号:
fn process_instruction()
我们可以通过在括号内列出变量名称及其数据类型来将参数(输入)添加到我们的函数中。
Rust 是一种“静态类型”语言,这意味着每个值都有一个特定的类型(如文本、数字等),Rust 在你的程序运行之前就知道这些类型。 如果 Rust 无法自行确定类型,你需要明确地告诉它。
在我们的“Hello, world!”程序中,我们将使用一个名为 process_instruction 的函数,该函数需要三个参数:
program_id:它的类型为 &Pubkey。accounts:它的类型为 &[AccountInfo]。instruction_data:它的类型为 &[u8]。请注意每个类型前面的 & 吗?在 Rust 中,& 表示你正在创建对另一个变量的“引用”。 这让你的函数可以使用一个值,而无需“拥有”它。 把它想象成看一本书,而没有从别人手中拿走它。 此操作称为“借用”。 当调用 process_instruction 时,它将“借用”提供的值,确保它们是正确的类型。
此外,&[AccountInfo] 和 &[u8] 周围的方括号 [] 表示这些参数期望“切片”。 切片就像列表的一部分,但事先不知道它的确切长度。 因此,accounts 和 instruction_data 可以接收各种长度的列表。
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
)
函数还可以返回值。 你在括号后的箭头 -> 之后指定返回类型。
我们的 process_instruction 函数将返回一个 ProgramResult 类型,我们将在接下来介绍。
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult
Result 是一种标准的 Rust 类型,可帮助你处理可能成功(Ok)或失败(Err)的情况。 我们稍后将深入探讨“枚举”(enumerations),但现在了解 Result 是好事,因为我们会在程序中看到 Ok。
当你使用 Ok 或 Err 时,你必须包含一个值。 此值的类型取决于你的代码的期望。 例如,如果一个函数需要返回一个 Result<String, i64>,它可以返回一个带有文本值(String)的 Ok,或者一个带有整数(i64)的 Err。 这里的 i64 可以是一个错误代码。
要用文本消息表示成功:
Ok(String::from("Success!"));
要用数字表示错误:
Err(404);
请记住,Solana 网络上的所有数据都存储在账户中。 每个帐户都有一个唯一的地址,可以帮助识别和访问其数据。 Solana 程序是一种特殊的 Solana 账户,用于存储在区块链上运行的实际代码。
solana_program Crate要用 Rust 编写 Solana 程序,我们使用 solana_program 库 crate。 将 solana_program 视为构建 Solana 程序的基本工具包。 它提供了所有必要的模块和工具。
对于一个基本程序,我们需要将以下项目从 solana_program crate 引入到我们的代码中:
use solana_program::{
account_info::AccountInfo, // 帮助我们获取有关帐户的信息(如地址、余额等)
entrypoint, // 一种特殊的工具,用于定义程序从哪里开始
entrypoint::ProgramResult, // 我们的程序将返回的结果类型
pubkey::Pubkey, // 帮助我们处理帐户地址
msg // 一种将消息打印到程序日志的简单方法
};
让我们分解一下这些操作的作用:
AccountInfo:一个结构,让我们能访问关于帐户的详细信息,例如其地址、所有者、持有的 Solana 数量、数据大小、是否可执行,以及它是否在当前交易中用于签名或写入。entrypoint:这是一种特殊的工具(宏),用于标记程序开始的函数。 它就像进入我们程序的指令的“前门”。ProgramResult:此类型位于 entrypoint 模块中,告诉我们程序是否成功运行(Result)或遇到错误(ProgramError)。Pubkey:pubkey 模块中的一个结构,允许我们使用帐户地址,即公钥。msg:另一种特殊工具(宏),允许我们打印消息。 这些消息会显示在程序的日志中,这对于调试很有帮助。每个 Solana 程序都需要一个入口点 —— 一个特定的函数,程序从该函数开始处理指令。 此入口点使用 entrypoint! 宏声明。
Solana 程序的入口点需要一个带有以下参数的 process_instruction 函数:
program_id:我们程序代码存储的帐户的地址。accounts:我们的程序需要访问的其他帐户的列表才能完成其工作。instruction_data:随指令附带的任何额外、特定的信息或命令。entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult;
务必记住,Solana 程序帐户仅存储程序的逻辑。 这意味着它们是“只读”和“无状态”的。 简单来说,它们本身不存储任何变化的数据。 “状态”(程序需要使用的实际数据)保存在单独的数据帐户中。
要运行指令,程序需要明确告知要使用哪些数据帐户,方法是通过 accounts 参数传递它们。 任何其他输入都通过 instruction_data 参数传递。
程序运行完成后,必须返回 ProgramResult 类型的值。 此类型是一个 Result,其中成功的结果由 () 表示(表示“无”或空值),失败的结果是 ProgramError(solana_program crate 中定义的错误类型)。
你现在已经了解了如何从头开始用 Rust 构建基本的 Solana 程序。 通过了解模块、crates、路径以及 process_instruction 的工作原理,你已经获得了坚实的基础。 这种底层方法使你能更好地控制并更清楚地了解 Solana 程序如何运行。 从这里,你可以开始探索更高级的功能并进一步提高你的技能。
如果你对去中心化基础设施、链上数据系统或使用预言机网络构建真实世界的项目感兴趣,请关注:
- 原文链接: blog.blockmagnates.com/b...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!