在上文Solana项目学习(一):Hello World_biakia0610的专栏-CSDN博客中,我们学习了solana的helloworld例子,在代码最后一部分,有一个非常复杂的表达式:
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
这里的&mut &mut account.data.borrow_mut()[..] 到底是返回什么呢? 这篇文章会尝试进行详细探讨。
首先,account是solana官方数据类型AccountInfo
#[derive(Clone)]
pub struct AccountInfo<'a> {
/// Public key of the account
pub key: &'a Pubkey,
/// Was the transaction signed by this account's public key?
pub is_signer: bool,
/// Is the account writable?
pub is_writable: bool,
/// The lamports in the account. Modifiable by programs.
pub lamports: Rc<RefCell<&'a mut u64>>,
/// The data held in this account. Modifiable by programs.
pub data: Rc<RefCell<&'a mut [u8]>>,
/// Program that owns this account
pub owner: &'a Pubkey,
/// This account's data contains a loaded program (and is now read-only)
pub executable: bool,
/// The epoch at which this account will next owe rent
pub rent_epoch: Epoch,
}
我们可以看到data是个Rc<RefCell<&'a mut u64>>类型。那么当我们调account.data.borrow_mut()的时候,实际发生了什么呢?
在RC官方文档中有如下描述:
//! `Rc<T>` automatically dereferences to `T` (via the [`Deref`] trait),
//! so you can call `T`'s methods on a value of type [`Rc<T>`][`Rc`]. To avoid name
//! clashes with `T`'s methods, the methods of [`Rc<T>`][`Rc`] itself are associated
//! functions, called using [fully qualified syntax]:
所以account.data.borrow_mut()调用的其实是被RC包裹的RefCell<&mut [u8]>的方法。
下面我们看下RefCell中的borrow_mut()
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
#[track_caller]
pub fn borrow_mut(&self) -> RefMut<'_, T> {
self.try_borrow_mut().expect("already borrowed")
}
可以看到返回的是一个RefMut类型。因此,account.data.borrow_mut()[..]其实是对RefMut类型做[..]操作,而[..]操作是什么呢?它其实是语法糖,真实的操作是
*(account.data.borrow_mut().index_mut(..))
这时候其实是调用RefMut的index_mut(..)方法,然后找了一圈发现RefMut并没有这个方法。这时候按理说应该会编译报错啊,但是实际是运行成功的。那为什么呢?其实是Rust编译器帮我们智能解析了表达式,下面是Rust官方文档中的描述:
大意就是Rust会对不匹配的表达式进行各种尝试,比如引用、解引用等。
*(account.data.borrow_mut().index_mut(..))
这里虽然RefMut并没有index_mut方法,但是Rust会对RefMut进行解引用,得到内部的&mut [u8],而&mut [u8]是可以执行index_mut方法的,返回的还是个&mut [u8]。因此上面的表达式就变成了
*(&mut [u8])
最终就是一个[u8]类型
因此&mut &mut account.data.borrow_mut()[..]的结果是&mut &mut [u8]。
参考:
rust - Trouble understanding &mut &mut reference - Stack Overflow
Method call expressions - The Rust Reference
IndexMut in std::ops - Rust