本文介绍了如何使用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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!