0

I'm working on porting a Typescript library which has this type alias and function:

type GlkTypedArray = Uint8Array | Uint32Array
get_buffer(buf: GlkTypedArray): number

Pretty straight forward, it accepts either a Uint8Array or a Uint32Array.

I'm now trying to do the same in Rust, via a trait that is defined only for u8 and u32:

trait GlkInt: Sized {}
impl GlkInt for u8 {}
impl GlkInt for u32 {}

fn get_buffer(buf: &mut [dyn GlkInt]) -> u32;

But unfortunately it's not allowing this, saying that the trait is not Sized, even though I thought that trait definition meant it would be.

error[E0277]: the size for values of type `dyn misc::GlkInt` cannot be known at compilation time
  --> remglk/src/glkapi/streams.rs:18:29
   |
18 |     fn get_buffer(buf: &mut [dyn GlkInt]) -> u32;
   |                             ^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `dyn misc::GlkInt`
   = note: slice and array elements must have `Sized` type

error[E0038]: the trait `misc::GlkInt` cannot be made into an object
  --> remglk/src/glkapi/streams.rs:18:30
   |
18 |     fn get_buffer(buf: &mut [dyn GlkInt]) -> u32;
   |                              ^^^^^^^^^^ `misc::GlkInt` cannot be made into an object
   |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> remglk/src/common/misc.rs:13:19
   |
13 | pub trait GlkInt: Sized {}
   |           ------  ^^^^^ ...because it requires `Self: Sized`
   |           |
   |           this trait cannot be made into an object...

Do I need to do something else, or is this whole approach not going to work? What else can I try?

curiousdannii
  • 1,658
  • 1
  • 25
  • 40
  • 1
    Is it helpful to use GlkTypedArray as enum with 2 arms (u8 and u32) ? – Ramin Rahimzada Dec 18 '22 at 06:25
  • @RaminRahimzada If the enum is for the integer type, wouldn't that require a discriminant for every entry in the array? If so that would mean I couldn't just pass a slice (`&[u8]` or `&[u32]`) into the function. If the enum was for the entire array, I think it would have to be an enum with `Vec` and `Vec` instead of just a slice? I think passing a Vec would require cloning the slice, which won't work because I need to modify the original slice in place. – curiousdannii Dec 18 '22 at 06:28
  • You can use an enum on the whole slice (`enum E<'a> { U8(&'a mut [u8]), U32(&'a mut [u32]) }`). – Chayim Friedman Dec 18 '22 at 07:49
  • What do you need to do with each element? – Chayim Friedman Dec 18 '22 at 07:50
  • 1
    I don’t think you need dynamic dispatch here. Why don’t you just use static dispatch or just enums? – Miiao Dec 18 '22 at 08:43
  • `fn get_buffer(buf: &mut [T]) -> u32;` but you will need to also declare the operations you want to use inside the function (e.g. `trait GlkInt: Add + Mul + …`) or declare `trait GlkInt: Into` and use `buf[…].into()` to convert the items to `u32` when you use them. – Jmb Dec 18 '22 at 09:41
  • @ChayimFriedman Ah, for some reason I thought I couldn't put a slice in an enum like that, only a Vec. Yes I think that's the best option. – curiousdannii Dec 18 '22 at 12:00

1 Answers1

5

The size of elements of an array/slice need to be known at compile time so the compiler can calculate the distance between 2 items for indexing. a dyn GlkInt would be either 1 or 4 bytes wide so there is no way to know the size.

You can either just do stattic dispatch using a generic type in wihch case you probably want to require more traits on your generic (From<u8>, Into<u32> come to mind):

trait GlkInt {}
impl GlkInt for u8 {}
impl GlkInt for u32 {}

fn get_buffer<T: GlkInt>(buf: &mut [T]) -> u32 {
    0
}

Or use an enum and implement the different code paths yourself.

use GlkSlice::*;
enum GlkSlice<'a> {
    U8(&'a mut [u8]),
    U32(&'a mut [u32]),
}

fn get_buffer(buf: GlkSlice<'_>) -> u32 {
    match buf {
        U8(buf) => buf[0] as u32,
        U32(buf) => buf[0],
    }
}
cafce25
  • 15,907
  • 4
  • 25
  • 31