Unsafe code traits problem with generic inference

I'm trying to fix some code no longer maintained by someone else. Here's my fork:GitHub - John-Nagle/rend3-hp: Fork of Rend3 because Rend3 has moved to maintenance mode. in branch wgpu22.

The compile error is:

error[E0283]: type annotations needed
   --> rend3/src/util/scatter_copy.rs:116:17
    |
116 |                 bytemuck::cast_slice_mut(&mut mapped_slice[range_start + 1..range_end]),
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `B` declared on the function `cast_slice_mut`
    |
    = note: cannot satisfy `_: NoUninit`
    = help: the following types implement trait `NoUninit`:
              NonZero<i128>
              NonZero<i16>
              NonZero<i32>
              NonZero<i64>
              NonZero<i8>
              NonZero<isize>
              NonZero<u128>
              NonZero<u16>
            and 6 others
note: required by a bound in `bytemuck::cast_slice_mut`
   --> /home/john/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytemuck-1.16.1/src/lib.rs:368:6
    |
366 | pub fn cast_slice_mut<
    |        -------------- required by a bound in this function
367 |   A: NoUninit + AnyBitPattern,
368 |   B: NoUninit + AnyBitPattern,
    |      ^^^^^^^^ required by this bound in `cast_slice_mut`
help: consider specifying the generic arguments
    |
116 |                 bytemuck::cast_slice_mut::<u32, B>(&mut mapped_slice[range_start + 1..range_end]),
    |                                         ++++++++++

This used to compile, but a recent update to "encase" changed an internal fn, "Writer", which is not supposed to be used by clients of the crate. That fn, in encase/core/rw.rs had type BufferMut change from [u8] to MaybeUninit. This broke the type inference in scatter_copy.

The code that won't compile is in a generic function. It passes a generic function as an argument to another generic function. The Rust inference engine has to sort this out. This is unsafe code doing what's effectively pointer manipulation for loading data into a GPU.

            let mut writer = encase::internal::Writer::new(
                &item.data,
                bytemuck::cast_slice_mut(&mut mapped_slice[range_start + 1..range_end]),
                0,
            )
            .unwrap();

cast_slice_mut is a generic function where the second generic parameter, "B", is determined entirely by the output from the function. Backward inference has to determine the type of the output. The second parameter to Writer::new is also generic, and not constraining enough to satisfy cast_slice_mut's requirements. So inference failed.

The compiler-suggested fix,

bytemuck::cast_slice_mut::<u32, B>(&mut mapped_slice[range_start + 1..range_end]),

won't work because there is no "B" in scope at that point.

However, what we want there is basically &mut [u8]. That won't work; the function wants its trait BufferMut.

This is delicate code, and I need to fix it without breaking it. There's a lot of clever type inference going on, and, of course, few comments.

First, can I do this through the published API of "encase" without losing all that genericity? If not, what's a clean way to fix this?

Try this:

bytemuck::cast_slice_mut::<_, u8>(...)

The path I took to get there:

Diagnostics for the inline case could be improved by telling you that you need to replace B, like they do in other cases.

365 | pub fn cast_slice_mut<
    |        -------------- required by a bound in this function
366 |   A: NoUninit + AnyBitPattern,
367 |   B: NoUninit + AnyBitPattern,
    |      ^^^^^^^^ required by this bound in `cast_slice_mut`
help: consider giving `cast` an explicit type, where the type for type parameter `B` is specified
    |
14  |     let cast: &mut [B] = bytemuck::cast_slice_mut(slice);
    |             ++++++++++

Thanks.

(I tried u8 before, saw I got a huge number of errors, and backed it out. What had happened was that I got past this error and into the next compile, which had hit a crates out of sync problem. My bad.)

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.