Search code examples
rustblockchainsubstrate

How do you convert between Substrate specific types and Rust primitive types?


Using the Substrate blockchain framework, how can I convert between Substrate specific types and Rust primitive types and vice versa?

For example:

  • Convert a time (T::Moment) to a u64
  • Convert a u64 to a T::Balance

etc...


Solution

  • For the latest in Substrate master

    Substrate has removed As in favor of From/Into. An assumption is made that all types are at least u32.

    From the trait SimpleArithmatic, the following are implemented:

    • From: u8, u16, u32
    • TryFrom: u64, u128, usize
    • TryInto: u8, u16, u32, u64, u128, usize

    Another trait is also provided to provide ergonomic infallible conversion when you don't care if the value saturates.

    • UniqueSaturatedInto: u8, u16, u32, u64, u128
    • UniqueSaturatedFrom: u64, u128

    NOTE on SaturatedConversion from Gav

    SaturatedConversion (saturated_into and saturated_from) should not be used unless you know what you're doing, you've thought and considered all options and your use-case implies that saturation is fundamentally correct. The only time I imagine this is the case is deep in runtime arithmetic where you are logically certain it will not overflow, but can't provide a proof because it would depend on consistent pre-existing state.

    This means that working from u32 to Substrate specific types should be easy:

    pub fn u32_to_balance(input: u32) -> T::Balance {
        input.into()
    }
    

    For larger types, you need to handle the case where the Balance type for a runtime is smaller than what is available:

    pub fn u64_to_balance_option(input: u64) -> Option<T::Balance> {
        input.try_into().ok()
    }
    
    // Note the warning above about saturated conversions
    pub fn u64_to_balance_saturated(input: u64) -> T::Balance {
        input.saturated_into()
    }
    

    When converting from T::Balance to a rust primitive, you need to also handle conversion between incompatible types:

    pub fn balance_to_u64(input: T::Balance) -> Option<u64> {
        TryInto::<u64>::try_into(input).ok()
    }
    
    // Note the warning above about saturated conversions
    pub fn balance_to_u64_saturated(input: T::Balance) -> u64 {
        input.saturated_into::<u64>()
    }
    

    For Substrate v1.0

    Substrate provides pub trait As<T> in the sr-primitives crate:

    /// Simple trait similar to `Into`, except that it can be used to convert numerics between each
    /// other.
    pub trait As<T> {
        /// Convert forward (ala `Into::into`).
        fn as_(self) -> T;
        /// Convert backward (ala `From::from`).
        fn sa(_: T) -> Self;
    }
    

    Here are some working examples of how it can be used:

    impl<T: Trait> Module<T> {
        // `as_` will turn T::Balance into a u64
        pub fn balance_to_u64(input: T::Balance) -> u64 {
            input.as_()
        }
    
        // Being explicit, you can convert a `u64` to a T::Balance
        // using the `As` trait, with `T: u64`, and then calling `sa`
        pub fn u64_to_balance(input: u64) -> T::Balance {
            <T::Balance as As<u64>>::sa(input)
        }
    
        // You can also let Rust figure out what `T` is
        pub fn u64_to_balance_implied(input: u64) -> T::Balance {
            <T::Balance as As<_>>::sa(input)
        }
    
        // You can also let Rust figure out where `sa` is implemented
        pub fn u64_to_balance_implied_more(input: u64) -> T::Balance {
            T::Balance::sa(input)
        }
    }