本节内容,不再简单描述怎么在链上写HelloWorld,而是把难度增加一个,怎么去存储一个结构体数据。Rust基础知识在实现本节实战内容之前,我们需要对Rust基础知识进行学习变量let关键字表示变量,默认情况下是不可变的,添加mut关键字,意味着其中存储的值是可变的。结构体结
本节内容,不再简单描述怎么在链上写Hello World,而是把难度增加一个,怎么去存储一个结构体数据。
在实现本节实战内容之前,我们需要对Rust基础知识进行学习
let关键字表示变量,默认情况下是不可变的,添加mut关键字,意味着其中存储的值是可变的。
结构体(Struct)是一种自定义数据类型,允许将多个相关不同类型的字段打包在一起,形成一个有意义的组
struct User{
active: bool,
email: String,
age: u64
}
枚举类型是一种数据结构,允许通过列举变体来定义一个类型。
enum LightStatus {
On,
Off
}
match允许将一个值与一系列模式进行比较,然后根据匹配的模式执行代码。
enum Coin {
Penny,
Nickel,
Dime,
Quarter
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25
}
}
在Rust中,impl关键字定义类型的实现。函数和常量都可以在实现中定义。
struct Example {
number: i32
}
impl Example {
fn boo() {
println!("boo! Example::boo() was called!");
}
fn answer(&mut self) {
self.number += 42;
}
fn get_number(&self) -> i32 {
self.number
}
}
特征描述了类型可实现的抽闲接口,如果特征定义了一个函数bark()
, 而一个类型采用该特征,那么该类型必须实现bark()
函数
指令数据一般以字节数组的形式传递给程序,在之前的单元中,我们使用Borsh进行客户端的序列化和反序列化。要在程序端使用Borsh,我们使用borsh crate。这个Crate提供了BorshDeserialize和BorshSerialize的traits。
为了使反序列化指令数据简单化,可以创建一个表示数据的结构体,并使用derive将属性BorshDeserialize trait应用到结构体上。这将实现BorshDeserialize
中定义的方法,包括我们将使用来反序列化指令数据的try_from_slice
方法
#[derive(BorshDeserialize)]
struct NoteInstructionPayload {
id: u64,
title: String,
body: String
}
一旦创建这个结构体,可以为指令枚举创建一个实现,用于处理与反序列化指令数据相关的逻辑。通常会在一个名为unpack
的函数内完成这个操作,该函数接收指令数据作为参数,并返回带有反序列化数据的适当枚举实例。
impl NodeInstruction {
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
let (&variant, rest) = input.split_first().ok_or(ProgramError::InvalidstructionData)?;
let payload = NoteInstructionPayload::try_from_slice(rest).unwrap();
Ok(match variant{
0 => Self::CreateNode {
title: payload.title,
body: payload.body,
id: payload.id
},
1 => Self::UpdateNode {
title: payload.title,
body: payload.body,
id: payload.id
},
2=> Self::DeleteNode {
title: payload.id
},
_ => return Err(ProgramError::InvalidInstructionData)
})
}
}
上面的示例主要包含三部分:
1.这个函数首先在input
参数上使用split_first
函数,返回一个元组。第一个元素variant
是字节数组的第一个字节,第二个元素rest
是字节数组的剩余部分。
2.然后,函数使用NoteInstructionPayload
上的try_from_slice
方法,将字节数组的剩余部分反序列化为一个名为payload
的NoteInstructionPayload
实例
3.函数使用match
表达式在variant
上创建并返回适当的枚举实例,使用payload
中信息
程序的核心逻辑,其实就是将数据反序列化为自定义Rust类型的方法,
例如,下面的代码
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8]
) -> ProgramResult {
let instruction = NoteInstruction::unpack(instruction_data)?;
match instruction {
NoteInstruction::CreateNode {title, body, id} => {
},
NoteInstruction::UpdateNode{title, body, id} =>{
},
NoteInstruction::DeleteNode{id} => {
}
}
}
我们将最后的合约代码分为两部分,分别为lib.rs 和 instruction.rs
lib.rs
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
};
pub mod instruction;
use instruction::MovieInstruction;
entrypoint!(process_instruction);
pub fn add_movie_review(
program_id: &Pubkey,
accounts: &[AccountInfo],
title: String,
rating: u8,
description: String,
) -> ProgramResult {
msg!("Adding movie review");
msg!("Title: {} ", title);
msg!("Rating: {}", rating);
msg!("Description: {}", description);
Ok(())
}
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
let instruction = MovieInstruction::unpack(instruction_data)?;
match instruction {
MovieInstruction::AddMovieReview {
title,
rating,
description,
} => add_movie_review(program_id, accounts, title, rating, description),
}
}
instruciton.rs
use borsh::BorshDeserialize;
use solana_program::program_error::ProgramError;
pub enum MovieInstruction {
AddMovieReview {
title: String,
rating: u8,
description: String,
},
}
#[derive(BorshDeserialize)]
struct MovieReviewPayload {
title: String,
rating: u8,
description: String,
}
impl MovieInstruction {
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
let (&variant, rest) = input
.split_first()
.ok_or(ProgramError::InvalidInstructionData)?;
let payload: MovieReviewPayload = MovieReviewPayload::try_from_slice(rest).unwrap();
Ok(match variant {
0 => Self::AddMovieReview {
title: payload.title,
rating: payload.rating,
description: payload.description,
},
_ => return Err(ProgramError::InvalidInstructionData),
})
}
}
部署完成后,查看链上浏览器地址:
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!