#traits #const-fn #stable #supertraits #macro

supertrait

Supertrait enables default associated types and const fn trait items in stable Rust

2 unstable releases

0.1.0 Aug 2, 2023
0.0.1 Jul 13, 2023

#2099 in Rust patterns

21 downloads per month

MIT license

21KB
176 lines

Supertrait 🦹

Crates.io docs.rs Build Status MIT License

Supertrait is a revolutionary crate that enables default associated types and const fn trait items in stable Rust as of July 2023. Supertrait accomplishes this through a variety of macro-related techniques including the use of macro_magic as well as the "module wormhole" technique demonstrated in the docs for #[supertrait] and #[impl_supertrait].

Here is an end-to-end example:

#[supertrait]
pub trait Fizz<T: Copy>: Copy + Sized {
    type Foo = Option<T>;
    type Bar;

    const fn double_value(val: T) -> (T, T) {
        (val, val)
    }

    const fn triple_value(val: T) -> (T, T, T);

    fn double_self_plus(&self, plus: Self::Foo) -> (Self, Self, Self::Foo) {
        (*self, *self, plus)
    }

    const fn interleave<I>(&self, a: T, b: I) -> (I, Self::Foo, T);
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
struct Buzz;

#[impl_supertrait]
impl<T: Copy> Fizz<T> for Buzz {
    type Bar = usize;

    const fn triple_value(val: T) -> (T, T, T) {
        (val, val, val)
    }

    const fn interleave<I>(&self, a: T, b: I) -> (I, Self::Foo, T) {
        (b, Some(a), a)
    }
}

#[test]
const fn test_buzz_const() {
    assert!(Buzz::triple_value(3).0 == 3);
    let buzz = Buzz {};
    match buzz.interleave('h', false).1 {
        Some(c) => assert!(c == 'h'),
        None => unreachable!(),
    }
}

#[test]
fn test_buzz_default_associated_types() {
    let buzz = Buzz {};
    assert_eq!(buzz.double_self_plus(Some(3)), (buzz, buzz, Some(3)))
}

Notice that in the above supertrait we are able to use both default associated types and const fn trait items with full generics support.

Supertraits are also sealed such that a trait created via #[supertrait] can only be impled if #[impl_supertrait] is attached to the impl statement.

Default associated types are implemented in a way that should be nearly identical with how default associated types will function when they are eventually added to stable rust.

Const fn trait items are implemented as inherents on the underlying type, however their presence is enforced by #[impl_supertrait] and their type bounds are enforced by the requirement for shadow non-const implementations of each const fn trait item that are filled in by the expansion of #[impl_supertrait]. These two mechanisms along with the trait sealing technique mentioned above collectively ensure that const fn trait items presence and correctness is enforced just as strongly as that of regular trait items.

Using inherents as the vehicle for implementing const fn trait items has a few drawbacks due to the naming collisions that can occur with existing inherent items as well as the inability to blanket impl supertraits containing const fns (because it is impossible in stable Rust to blanket impl anything other than a real trait).

That said, inherents are a convenient fallback when you find yourself reaching for const fn items in traits, and supertrait contains the most convenient implementation of this behavior currently possible in stable Rust.

Dependencies

~1.5–2.1MB
~45K SLoC