#extension #trait

macro dev extend

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

8 unstable releases (3 breaking)

0.3.0 Aug 31, 2020
0.2.1 Aug 29, 2020
0.1.2 May 22, 2020
0.1.1 Feb 22, 2020
0.0.2 Oct 30, 2019

#124 in Rust patterns

Download history 7171/week @ 2020-08-11 6519/week @ 2020-08-18 6307/week @ 2020-08-25 6184/week @ 2020-09-01 8041/week @ 2020-09-08 7809/week @ 2020-09-15 10076/week @ 2020-09-22 8578/week @ 2020-09-29 8735/week @ 2020-10-06 9142/week @ 2020-10-13 8449/week @ 2020-10-20 7282/week @ 2020-10-27 8000/week @ 2020-11-03 6718/week @ 2020-11-10 7842/week @ 2020-11-17 6009/week @ 2020-11-24

29,037 downloads per month
Used in 117 crates (3 directly)

MIT license

16KB
312 lines

extend

API documentation

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
    }
}

fn main() {
    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. The default visibility is private. Example: #[ext(pub)]. This must be the first argument to the attribute.
  • 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(pub, name = ResultSafeUnwrapExt)]
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()
    }
}

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.5–0.9MB
~22K SLoC