'How to borrow ctx in a module chain
I have this entrypoint where ctx is passed by a parent:
pub mod instructions;
#[program]
pub mod solana_anchor_coinflip {
use super::*;
pub fn flip(ctx: Context<Play>, data: FlipArgs) -> Result<()> {
instructions::play::play(ctx, data)
}
}
Then, instructions/play.rs has this:
pub fn play(ctx: Context<Play>, data: FlipArgs) -> Result<()> {
ctx.accounts.game.flip(ctx, data) // <== cannot move out of `ctx` because it is borrowed
}
#[derive(Accounts)]
pub struct Play<'info> {
#[account(mut)]
pub game: Account<'info, Game>,
pub player: Signer<'info>,
}
and ctx is finally passed to game.rs:
impl Game {
pub fn flip(&mut self, ctx: Context<Play>, data: FlipArgs) -> Result<()> {
self.charge_fee(ctx);
match data.heads_or_tails {
true => self.play_heads(ctx), // <== use of moved value: `ctx`
false => self.play_tails(ctx),
}
}
fn charge_fee(&self, ctx: Context<Play>) -> Result<()> {
let player = &ctx.accounts.player;
// ...
Ok(())
}
}
How to correctly borrow ctx from lib.rs > play.rs > game.rs?
Solution 1:[1]
As said in the comments, you are not borrowing ctx, you're just moving it. See the relevant documentation to understand the differences, alongside with examples to illustrate that.
If you read through that, you will understand why you just need to change the signature of your functions:
// in instructions/play.rs
pub fn play(ctx: &Context<Play>, data: FlipArgs) -> Result<()> { // <-- takes a `&Context<Play>`
ctx.accounts.game.flip(ctx, data) // and passes the borrow
}
// game.rs
impl Game {
pub fn flip(&mut self, ctx: &Context<Play>, data: FlipArgs) -> Result<()> { // <-- takes a &Context<Play>
self.charge_fee(ctx); // <-- here you pass the borrow
// also, unused `Result`, which is _bad_
if data.heads_or_tails { // an `if` statement is a `match` over a `bool`
self.play_heads(ctx) // <-- here you pass the borrow too, which is fine, because `&T: Copy`
} else {
self.play_tails(ctx) // <-- same as above
}
}
fn charge_fee(&self, ctx: &Context<Play>) -> Result<()> { // <-- takes a `&Context<Play>`
let player = &ctx.accounts.player;
// ...
Ok(())
}
}
However, despite this patch, there is a chance that you need to refactor your code anyways after understanding borrow, for multiple motives:
- Given the context of your question, it's impossible to tell if
playshould take borrow to aContext<Play>, or an owned value, because both could work (in the second case, you would have to pass a borrow to the method call, iectx.accounts.game.flip(&ctx, data). - You seem to pass
dataas an owned value all along, just like you do withctx, but since it was a mistake withctx, it might be fordatatoo (and for many other parts of your code). - I'm not sure whether the current version will compile anyways do the
fliprequiring&mut self, that is, it needs a mutable borrow toctx.accounts.gamein theplayfunction. However, that would also require a mutable borrow ofctx(unless it doesn't, but that's a bit too advanced: learn about borrows before learning about interior mutability) for the duration of the call, which would invalidate any borrow to be passed as an argument.
As an advice, I would suggest you to wrap your head around Rust's core concepts (which can be efficiently done by reading the Rust Book) before designing the architecture of a complex application, because Rust has some very peculiar patterns, even if you are used other programming languages. Otherwise, you will keep fighting against the compiler trying to adapt the code you had in mind when you still did not fully understand the borrowing and ownership in Rust to something that works.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
