Repository: capnproto/capnproto-rust (language: rust)

Issue:
Primitive list panicks if the list type is `Void`
In `primitive_list.rs`

```rust
    #[cfg(target_endian = "little")]
    /// Returns something if the slice is as expected in memory.
    pub fn as_slice(&self) -> Option<&[T]> {
        if self.reader.get_element_size() == T::element_size() {
            let bytes = self.reader.into_raw_bytes();
            Some(unsafe {
                use core::slice;
                slice::from_raw_parts(
                    bytes.as_ptr() as *mut T,
                    8 * bytes.len() / (data_bits_per_element(T::element_size())) as usize,
                )
            })
        } else {
            None
        }
    }
```
If `T` is `Void`, then `data_bit_per_element` will be 0 and this will panic. Could we detect this and return a special pointer?


Discussion / hints:

Fix patch:
diff --git a/capnp/src/primitive_list.rs b/capnp/src/primitive_list.rs
index a35658a46..0a25a3b83 100644
--- a/capnp/src/primitive_list.rs
+++ b/capnp/src/primitive_list.rs
@@ -117,13 +117,14 @@ impl<'a, T: PrimitiveElement> Reader<'a, T> {
     pub fn as_slice(&self) -> Option<&[T]> {
         if self.reader.get_element_size() == T::element_size() {
             let bytes = self.reader.into_raw_bytes();
-            Some(unsafe {
-                use core::slice;
-                slice::from_raw_parts(
-                    bytes.as_ptr() as *mut T,
-                    8 * bytes.len() / (data_bits_per_element(T::element_size())) as usize,
-                )
-            })
+            let bits_per_element = data_bits_per_element(T::element_size()) as usize;
+            let slice_length = if bits_per_element > 0 {
+                8 * bytes.len() / bits_per_element
+            } else {
+                // This is a List(Void).
+                self.len() as usize
+            };
+            Some(unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *mut T, slice_length) })
         } else {
             None
         }
@@ -175,11 +176,15 @@ where
     pub fn as_slice(&mut self) -> Option<&mut [T]> {
         if self.builder.get_element_size() == T::element_size() {
             let bytes = self.builder.as_raw_bytes();
+            let bits_per_element = data_bits_per_element(T::element_size()) as usize;
+            let slice_length = if bits_per_element > 0 {
+                8 * bytes.len() / bits_per_element
+            } else {
+                // This is a List(Void).
+                self.len() as usize
+            };
             Some(unsafe {
-                core::slice::from_raw_parts_mut(
-                    bytes.as_mut_ptr() as *mut T,
-                    8 * bytes.len() / (data_bits_per_element(T::element_size())) as usize,
-                )
+                core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut T, slice_length)
             })
         } else {
             None


Test patch:
diff --git a/capnp/tests/primitive_list_as_slice.rs b/capnp/tests/primitive_list_as_slice.rs
index 06b19dc5a..8369aa323 100644
--- a/capnp/tests/primitive_list_as_slice.rs
+++ b/capnp/tests/primitive_list_as_slice.rs
@@ -3,8 +3,24 @@
 use capnp::{message, primitive_list};
 
 #[test]
-pub fn scratch_space_heap_allocator() {
+pub fn primitive_list_as_slice() {
     let mut msg = message::Builder::new_default();
+
+    {
+        let mut void_list = msg.initn_root::<primitive_list::Builder<()>>(0);
+        assert_eq!(void_list.as_slice().unwrap().len(), 0);
+        assert_eq!(void_list.into_reader().as_slice().unwrap().len(), 0);
+    }
+
+    {
+        let mut void_list = msg.initn_root::<primitive_list::Builder<()>>(5);
+        assert_eq!(void_list.as_slice().unwrap(), &[(), (), (), (), ()]);
+        assert_eq!(
+            void_list.into_reader().as_slice().unwrap(),
+            &[(), (), (), (), ()]
+        );
+    }
+
     {
         let mut u8list = msg.initn_root::<primitive_list::Builder<u8>>(0);
         assert_eq!(u8list.as_slice().unwrap().len(), 0);