#extension #traits #type #boilerplate #own #don-t #ext

macro extend

Create extensions for types you don't own with extension traits but without the boilerplate

15 releases (7 stable)

1.2.0 Mar 18, 2023
1.1.2 Sep 2, 2021
1.1.1 Jul 12, 2021
1.0.1 Feb 14, 2021
0.1.0 Nov 26, 2019

#211 in Rust patterns

Download history 22163/week @ 2023-11-20 21391/week @ 2023-11-27 23490/week @ 2023-12-04 23092/week @ 2023-12-11 22493/week @ 2023-12-18 11647/week @ 2023-12-25 18646/week @ 2024-01-01 22970/week @ 2024-01-08 20454/week @ 2024-01-15 25690/week @ 2024-01-22 24804/week @ 2024-01-29 21992/week @ 2024-02-05 26855/week @ 2024-02-12 24652/week @ 2024-02-19 29446/week @ 2024-02-26 29295/week @ 2024-03-04

111,022 downloads per month
Used in 537 crates (26 directly)

MIT license

23KB
432 lines

extend

Crates.io Docs dependency status Build status maintenance-status

Create extensions for types you don't own with extension traits but without the boilerplate.

Example:

use extend::ext;

#[ext]
impl<T: Ord> Vec<T> {
    fn sorted(mut self) -> Self {
        self.sort();
        self
    }
}

fn main() {
    assert_eq!(
        vec![1, 2, 3],
        vec![2, 3, 1].sorted(),
    );
}

lib.rs:

Create extensions for types you don't own with extension traits but without the boilerplate.

Example:

use extend::ext;

#[ext]
impl<T: Ord> Vec<T> {
    fn sorted(mut self) -> Self {
        self.sort();
        self
    }
}

assert_eq!(
    vec![1, 2, 3],
    vec![2, 3, 1].sorted(),
);

How does it work?

Under the hood it generates a trait with methods in your impl and implements those for the type you specify. The code shown above expands roughly to:

trait VecExt<T: Ord> {
    fn sorted(self) -> Self;
}

impl<T: Ord> VecExt<T> for Vec<T> {
    fn sorted(mut self) -> Self {
        self.sort();
        self
    }
}

Supported items

Extensions can contain methods or associated constants:

use extend::ext;

#[ext]
impl String {
    const CONSTANT: &'static str = "FOO";

    fn method() {
        // ...
        # todo!()
    }
}

Configuration

You can configure:

  • The visibility of the trait. Use pub impl ... to generate pub trait .... The default visibility is private.
  • The name of the generated extension trait. Example: #[ext(name = MyExt)]. By default we generate a name based on what you extend.
  • Which supertraits the generated extension trait should have. Default is no supertraits. Example: #[ext(supertraits = Default + Clone)].

More examples:

use extend::ext;

#[ext(name = SortedVecExt)]
impl<T: Ord> Vec<T> {
    fn sorted(mut self) -> Self {
        self.sort();
        self
    }
}

#[ext]
pub(crate) impl i32 {
    fn double(self) -> i32 {
        self * 2
    }
}

#[ext(name = ResultSafeUnwrapExt)]
pub impl<T> Result<T, std::convert::Infallible> {
    fn safe_unwrap(self) -> T {
        match self {
            Ok(t) => t,
            Err(_) => unreachable!(),
        }
    }
}

#[ext(supertraits = Default + Clone)]
impl String {
    fn my_length(self) -> usize {
        self.len()
    }
}

For backwards compatibility you can also declare the visibility as the first argument to #[ext]:

use extend::ext;

#[ext(pub)]
impl i32 {
    fn double(self) -> i32 {
        self * 2
    }
}

async-trait compatibility

Async extensions are supported via async-trait.

Be aware that you need to add #[async_trait] below #[ext]. Otherwise the ext macro cannot see the #[async_trait] attribute and pass it along in the generated code.

Example:

use extend::ext;
use async_trait::async_trait;

#[ext]
#[async_trait]
impl String {
    async fn read_file() -> String {
        // ...
        # todo!()
    }
}

Other attributes

Other attributes provided below #[ext] will be passed along to both the generated trait and the implementation. See async-trait compatibility above for an example.

Dependencies

~0.4–0.8MB
~20K SLoC