Trait for enforcing type size constraints


#1

It would be helpful if there were a trait like SameSize<T>: Sized implemented for all U: Sized where T and U are the same size. In my use case, it’s to ensure that a closure is the same size as () so that I don’t need to box it. I can just use it as a stack allocated dynamically sized type (stack_dst).

Right now the only way to ensure that the closure is size zero is to try to transmute it to (), but this has a number of downsides:

  • it requires a macro (downstream from the API) most of the time because you cannot transmute unsubstituted type parameters
  • in order to enforce use of the macro you need a wrapper type
  • the transmute takes ownership, so you have to rely on LLVM optimizations to make it a NOP at runtime

#2

I think this is a good use case for static_assert. We had static_assert, but RFC 1096 removed it just before 1.0 because it was unsatisfactory (but it worked) and it was hoped that something better will come along. Sadly, that didn’t happen so far.

RFC issue 1146 is an issue to design that something better. Actually, the issue calls out “checking that the size of two structs is equal” as the main use case! I am not sure whether trait is the best interface for this, but I think you definitely should comment there.


#3

I don’t think that static_assert would’ve worked for this purpose, for several reasons:

  • size_of needs to be const-evaluable.
  • nested items cannot refer to the type parameters of things they contain, e.g. this fails to compile:
fn foo<T>() {
   static FOO: *const () = 0 as *const T as *const ();
}

fn main() {}

#4

Static assert doesn’t help, it needs to be a trait bound for the function using it to be parametric.


#5

Why don’t we just wait until type level integers and make it an associated type of the Sized trait?


#6

There is a loose plan to make it an associated constant of Sized.


#7

It would also be interesting if it were possible to expose (via traits somehow) that a type “fits” within another type, which afaik an associated constant wouldn’t give us alone (?). This is a more general desire of both my use case but also a lot of goofy tricks like stack_dst itself. Safe and efficient abstractions could be written which avoid allocations or reuse memory in tricky ways based on this principle.


#8

Well if static assert works the way I think it does, then you could always compare the associated const of one type to another at compile time.


#9

This came up while trying to improve num::PrimInt.

The problem is: How to use mem::transmute in a function implementation within a generic trait?

Right now this is hard (or impossible) since there is no easy way to constrain the trait types to be SameSize or Transmutable.

I think this could be solved by lifting part of the magic behind mem::transmute into a SameSize or Transmutable trait, and then constraining mem::transmute on it. This would allow user defined generic functions/methods/traits to constrain on it as well and easily be able to call mem::transmute from them.


#10

This is also potentially useful for Vec::map_in_place (which no longer exists)


#11

This should be possible now with macros 1.1, although I don’t think that the feature should come from libraries, this seems like a core feature. At least, in C++ I use A Lot static_assert to enforce correct API usage(although, for most of the things I use it for, Rust has trait bounds).


#12

I could use it too. In image processing libraries it’s often valuable to do conversions in-place, while leaving types very flexible to allow various color spaces and newtypes.