There is a TokenBalances struct defined using a nested map. I want to implement a the balance_of method which takes two keys: token, account as input, and returns the balance as output.
use std::collections::*;
type TokenId = u128;
type AccountId = u128;
type AccountBalance = u128;
#[derive(Default, Clone)]
struct TokenBalances {
balances: HashMap<TokenId, HashMap<AccountId, AccountBalance>>,
}
impl TokenBalances {
fn balance_of(&self, token: TokenId, account: AccountId) -> AccountBalance {
self.balances
.get(&token)
.cloned()
.unwrap_or_default()
.get(&account)
.cloned()
.unwrap_or_default()
}
}
fn main() {
println!("{}", TokenBalances::default().balance_of(0, 0)) // 0
}
It uses cloned twice to turn an Option<&T> to Option<T>
I'm aware of to_owned as an alternative to cloned, but in its docs it says
Creates owned data from borrowed data, usually by cloning.
I'm wondering if the clones are really necessary. Is there any idomatic way to rewrite the method without cloning twice? And are clones completely avoidable?
You can use
Option::and_then:The
and_thencall returns anOption<&AccountBalance>. This is then cloned/copied withcopied. This is fine as it's just au128right now. If it ever becomes a more complex type where copying isn't cheap, makebalance_ofreturn&AccountBalanceinstead. Then you can remove thecopied()call andunwrap_or(&0)for example.Last note: the
unwrap_or_defaultmight hint at a code smell. I don't know your context, but it might make more sense to actually returnOption<AccountBalance>from the method instead of defaulting to 0.