Crate specialize [] [src]

Experimental specialization macros.

Syntax is highly volatile and subject to change. The use of these macros requires an unstable rust compiler and the #![feature(specialization)] crate attribute.

constrain!()

constrain!() attempts to add additional trait bounds to a generic type.

use std::io::{Write, Read, Seek, SeekFrom, Cursor, Repeat};

fn generic_function<T: Read>(read: &mut T) {
    if let Some(read_seek) = constrain!(ref mut [read: Read + Seek] = Cursor<Repeat>) {
        read_seek.seek(SeekFrom::Start(0));
    } else if constrain!(type [T: Write]) {
        // ...
    }
}

Caveats and Notes

There are a few oddities in the above example...

  1. You must specify a name for the trait to be used behind the scenes for specialization, as in IsReadSeek. It may be called whatever you like.
  2. A default fallback type that implements the desired trait bounds must also be provided. This type must be concrete but will never be used or instantiated, and is only used to appease the type checker.
  3. The ref and mut keywords may be left out in order to move or immutably borrow the value.
  4. The read value part may be an expression if surrounded in parenthesis.

Concrete constrain!()

constrain!() can also be used to convert a generic type into a concrete type. Similar to Any::downcast_ref, but enforced at compile time and without requiring the Any or Reflect trait bounds.

fn generic_function<T: ?Sized>(value: &T) -> bool {
    if let Some(value_bool) = constrain!(ref value as bool) {
        *value_bool
    } else if constrain!(type T as i32) {
        true
    } else {
        false
    }
}

specialize! { }

Provides a type-level match statement with specialization bounds.

use std::io::{Read, BufRead, Write, copy, sink};

fn generic_function<T: Read>(read: &mut T) {
    specialize! {
        trait fn Trait::nonsensical[W: Write](&mut self, w: &mut W) -> bool where [W: Write];

        match impl['a, T: Read] for T where [T: Read] {
            where [T: BufRead] => self.fill_buf().and_then(|buf| w.write(buf)).is_ok(),
            impl[U: Into<u8> + Clone] where [T: BufRead + Iterator<Item=&'a U>, U: 'a] =>
                w.write(&self.cloned().map(Into::into).collect::<Vec<u8>>()).is_ok(),
            _ => copy(self, w).is_ok(),
        }
    }

    Trait::nonsensical(read, &mut sink());
}

Caveats

  1. This is not an inline statement or expression, and is instead used to generate a trait and method pair. This means the prototype must be specified up-front, and no variables will be automatically captured from the outside scope.
  2. Generic bounds and where clauses must be surrounded by [] rather than <> or similar due to macro parsing limitations.
  3. The specialization "more specific" rules must be followed in order to prevent conflicting trait impls.
  4. The various where [B: Bounds...] clauses may be omitted, and are mostly included here for syntax demonstration.