rust

How to implement fmt::Display on custom type?


I have this custom type:

pub type Address = [u8; 32];

I tried implementing fmt::Display for this type:

impl fmt::Display for Address {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let public_key = sr25519::Public::from_raw(self);
        let address = public_key.to_ss58check();
        write!(f,"{}",address)
    }
}

But I get this compile error:

error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
   --> core/linix-primitives/src/lib.rs:122:1
    |
122 | impl fmt::Display for Address {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate

I understand that to implement a trait I need to have one of two: either the definition of a type locally or a definition of a trait locally.

Well, I already defined the type locally:

pub type Address = [u8; 32];

So why am I getting a compile error?


Solution

  • The problem here is that the following does not declare a new type:

    pub type Address = [u8; 32];
    

    But instead a type alias, which is closer to a c-style typedef. This means that your code gets turned into the following:

    impl fmt::Display for [u8; 32] {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let public_key = sr25519::Public::from_raw(self);
            let address = public_key.to_ss58check();
            write!(f,"{}",address)
        }
    }
    

    And here, [u8; 32] isn't a local type. What you probably want to do is use what is called the newtype pattern.

    If you're doing FFI with the newtype and using pointer casts, you may want to add a #[repr] attribute to that so that your newtype behaves (in an ABI manner, not an API manner) the same as what it contains.