Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Extension traits

In Grug, we make use of the extension trait pattern, which is well explained by this video.

To put it simply, a Rust library has two options on how to ship a functionality: to ship a function, or to ship a trait.

For instance, suppose our library needs to ship the functionality of converting Rust values to strings.

Shipping a function

The library exports a function:

#![allow(unused)]
fn main() {
pub fn to_json_string<T>(data: &T) -> String
where
    T: serde::Serialize,
{
    serde_json::to_string(data).unwrap_or_else(|err| {
        panic!("failed to serialize to JSON string: {err}");
    })
}
}

The consumer imports the function:

#![allow(unused)]
fn main() {
use grug::to_json_string;

let my_string = to_json_string(&my_data)?;
}

Shipping a trait

The library exports a trait, and implements the trait for all eligible types.

The trait is typically named {...}Ext where "Ext" stands for extension, because the effectively extends the functionality of types that implement it.

#![allow(unused)]
fn main() {
pub trait JsonSerExt {
    fn to_json_string(&self) -> String;
}

impl<T> JsonSerExt for T
where
    T: serde::Serialize,
{
    fn to_json_string(&self) -> String {
        serde_json::to_string(data).unwrap_or_else(|err| {
            panic!("failed to serialize to JSON string: {err}");
        })
    }
}
}

The consumer imports the trait:

#![allow(unused)]
fn main() {
use grug::JsonSerExt;

let my_string = my_data.to_json_string()?;
}

Extension traits in Grug

We think the consumer's syntax with extension traits is often more readable than with functions. Therefore we use this pattern extensively in Grug.

In grug-types, we define functionalities related to hashing and serialization with following traits:

  • Borsh{Ser,De}Ext
  • Proto{Ser,De}Ext
  • Json{Ser,De}Ext
  • HashExt

Additionally, there are the following in grug-apps, which provides gas metering capability to storage primitives including Item and Map, but they are only for internal use and not exported:

  • MeteredStorage
  • MeteredItem
  • MeteredMap
  • MeteredIterator