Small box optimization via const generics

Is this intended to become possible? [playground]

#![feature(const_generics)]

use std::mem::size_of;

struct MaybeBox <T : Sized> {
    inline: [    T ; (size_of::<T>() <= size_of::<Box<T>>()) as usize],
    boxed:  [Box<T>; (size_of::<T>() >  size_of::<Box<T>>()) as usize],
}

(Please ignore the implications on alignment, that is a separate issue.)

This currently fails with error: constant expression depends on a generic parameter; note: this may fail depending on what value the parameter takes.

Is this intended to eventually work? If so, what's currently holding it back from working? If not, what prevents this from working? Is there a way to work around it? Should we provide a way to do this? (Imho, yes.)

3 Likes

Note that even the simple case doesn't work yet,

playground

#![feature(const_generics)]

use std::mem::size_of;

struct Bytes<T> {
    bytes: [u8; size_of::<T>()]
}
2 Likes

Thankfully there is a workaround, type aliases to the rescue! playground

oh Rust, why does the more complex case work over the simple case?

edit: after a bit more testing, you can't actually use this type. It runs into an ICE. I think that const-generics just can't handle general expressions just yet. This makes sense, since we only got usable const-generics a little while ago, so general expressions are probably still in the pipeline.

To work around this, you have to use SmallBoxInner directly, and supply the correct size. You can sprinkle asserts around to check the size at the beginning of every function on SmallBoxInner.

This is a bit unfortunate, but that's how it is right now. That kinda kills the usecase of a generic type that can be seemlessly used as either a value unless it is too big, in which case it is boxed.

I guess you could just use the following type for that

struct SmallBox<T> {
    ptr: *const T,
    align: [T; 0],
    dropck: PhantomData<T>
}

edit2:

You may be interested in

2 Likes

I have every expectation that something like this will eventually be possible, but bounding on expressions like that is not coming super-soon, AFAIK, as it starts to hit expression equivalence questions that were explicitly out-of-scoped before.

Let's get the basics reliable and stabilized first. You can always do MaybeBox with unsafe for now.

1 Like

Const generic bounds are hard, but @CAD97's example is not a bound. It's just a fancy expression in an array length.

Edit: It's also suboptimal in case T has alignment larger than a pointer, since even a 0-sized array has alignment requirements, but what can you do.

2 Likes

(may be a little off topic, but)

pub struct Layout<T>(T);
trait Pick<const Bool: bool> {
    type Choice;
}

impl<T> Pick<true> for Layout<T> {
    type Choice = T;
}

impl<T> Pick<false> for Layout<T> {
    type Choice = Box<T>;
}

This runs into the bounds problems for const-generics, you may be able to use specialization to get around this

pub struct Layout<T>(T);
trait Pick<const Bool: bool> {
    type Choice;
}
impl<T, const Bool: bool> Pick<Bool> for Layout<T> {
    default type Choice = Box<T>;
}

impl<T> Pick<true> for Layout<T> {
    type Choice = T;
}

This way if we fail to unify with true for whatever reason, we can fall back on the general case. Although this logic may be faulty, as the interaction between const generics and specialization aren't well specified.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.