Method call syntax

Rust is not an object oriented programming language because it does not support inheritcance, which is a bad practice because the saying goes like "Prefer Composition over inheritance".

If you are familiar with OOP languages you would've benefitted mightily from the method call syntax as IDE's can implement auto-completion for fields and methods.

So in Rust we get the benefits of method call syntax without the cons of spaghetti code which is the eventual result of preferring inheritance.

Also inheritance affects performance and memory usage negatively.

How to achieve method call syntax in rust?

Let's review the bitcoin crate to understand this.

pub struct Amount(u64);

impl Amount {
    /// The zero amount.
    pub const ZERO: Amount = Amount(0);
    /// Exactly one satoshi.
    pub const ONE_SAT: Amount = Amount(1);
    /// Exactly one bitcoin.
    pub const ONE_BTC: Amount = Self::from_int_btc(1);
    /// The maximum value allowed as an amount. Useful for sanity checking.
    pub const MAX_MONEY: Amount = Self::from_int_btc(21_000_000);

    /// Create an [Amount] with satoshi precision and the given number of satoshis.
    pub const fn from_sat(satoshi: u64) -> Amount { Amount(satoshi) }

    /// Gets the number of satoshis in this [`Amount`].
    pub fn to_sat(self) -> u64 { self.0 }

    /// Convert from a value expressing bitcoins to an [Amount].
    pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
        Amount::from_float_in(btc, Denomination::Bitcoin)
    }
    ...
}

using method call syntax

// Accessing fields, called `associated constants`
let zero = Amount::ZERO;
let btc = Amount::ONE_BTC;

// Using `associated functions`
let btc_amount = Amount::from_btc(100.021);
let btc_u64_method_style = btc.to_sat(); // this is the preferred way of using associated functions with self
// while using the to_sat method the `btc: Amount` is passed to the self argument

let btc = Amount::ONE_BTC;
let btc_u64_func_style = Amount::to_sat(btc); // Not used widely, but legal
assert_eq!(btc_u64_method_style, btc_u64_func_style); // TRUE

Full source code can be found here.

The pub keyword suggests that the entity following can be used from anywhere, similar to public in Java or C#.

There are associated constants like ZERO, ONE_SAT etc.,

Why u64 is chosen?

Let's review why u64 is chosen but not u32 or u128 etc., The limit on the number of bitcoins in circulation is 21_000_000 which is 21_000_000 * 100_000_000 satoshis. One can quickly verify using python interpreter that 2^50 < 21_000_000 * 100_000_000 < 2^51. Which means we need at least 51 bits to store all possible satoshi values, hence 64 bit unsigned integer is used.

Encapsulation

You might wonder what is the purpose of this Amount type, it is just a wrapper over u64 right? Because this is how we achieve encapsulation in Rust. A library should make sure that the types defined are used as intended. u64 will behave like an unsigned integer, but satoshis should have the behaviours as defined in the bitcoin network and ecosystem.

The method call syntax ensures that the user gets to know everything on how to use the Amount type just by examining the function syntax and documentation, dig deeper into the code if necessary.

Last updated