rust-lang / rfcs

RFCs for changes to Rust
https://rust-lang.github.io/rfcs/
Apache License 2.0
5.78k stars 1.55k forks source link

Question/proposal: Struct specialization #3615

Closed Pscheidl closed 2 months ago

Pscheidl commented 2 months ago

Is it possible to have specialized versions of a structure, based on generic type's boundaries ? The point is to adjust struct's inner representation to match the nature of the generic type the best. In current state, two separate structures must be created, and the burden to choose the right one is on the library's user.

/// Basic implementation, putting no bounds on `T`.
struct Wrapper<T> {
    information: Vec<T>
}

/// Imaginary scenario, use HashSet for faster lookup internally when `T: Hash`
struct Wrapper<T> where T: Hash {
    information: HashSet<T>,
}

Implementations would have to be specific for each specialized struct variant, effectively making impl specializations mandatory in that case.

Other options

Traits + Impl specialization

Depends on RFC-1210.

Traits abstracting common functionality away. Would lead to dynamic dispatch.

struct Wrapper<T> {
    information: Box<dyn Collection<T>>,
}

impl<T> Wrapper<T> {
    fn new() -> Self {
        Self {
            information: Box::new(Vec::<T>::new()),
        }
    }
}

// Specialized impl for serialazble
impl<T> Wrapper<T> where T: Hash {
    fn new() -> Self {
        Self {
            information: Box::new(HashSet::<T>::new()),
        }
    }
}

trait Collection<T> {
    fn insert(&mut self, value: T);
}

impl<T> Collection<T> for Vec<T> {
    fn insert(&mut self, value: T) {
        self.push(value)
    }
}

impl<T> Collection<T> for HashSet<T> where T: Hash {
    fn insert(&mut self, value: T) {
        self.insert(value);
    }
}
kennytm commented 2 months ago

You don't need dynamic dispatch.

#![feature(specialization)]

trait Lookup: Sized {
    type Collection: Collection<Self>;
}

impl<T> Lookup for T {
    default type Collection = Vec<T>;
}

impl<T: Hash> Lookup for T {
    type Collection = HashSet<T>;
}

struct Wrapper<T: Lookup> {
    information: T::Collection,
}
Pscheidl commented 2 months ago

True, thank you. Doable once (if) Impl specialization arives then. It is quite verbose, but I can see the advantage of that. On the other hand, the approach ^ could be treated as a syntactic sugar ?

kennytm commented 2 months ago

the approach ^ could be treated as a syntactic sugar ?

At least the syntax proposed in OP is impossible, since without a concrete T you don't know the exact type of self.information and thus can't call any useful method. You still need that Collection trait and with that struct specialization syntax you can't associate the trait Collection to the member information anywhere.