4

Ok, this seems a bit silly, but I'm having trouble finding a function to return a statically sized array from the contents of a slice.

The Rust Book sections on arrays and slices says nothing about it. (It does show how to take a slice from an array, but I want to go the other way.) I also checked the documentation for std::slice and std::array, but if it's there, I'm not seeing it.

There is of course the option of writing out each element one by one, but that seems ridiculous. For now, I ended up writing a python one-liner to do it for me.

", ".join(["k[{}]".format(i) for i in range(32)])

So I ended up with this:

use db_key::Key;

#[derive(Clone)]
pub struct Sha256{
    bits : [u8;32]
}

impl Key for Sha256 {
    fn from_u8(k: &[u8]) -> Self {
        Sha256{bits:
               // FIXME: This is dumb.
               [ k[0], k[1], k[2], k[3], k[4], k[5], k[6], k[7], k[8], k[9], k[10], k[11], k[12], k[13], k[14], k[15], k[16], k[17], k[18], k[19], k[20], k[21], k[22], k[23], k[24], k[25], k[26], k[27], k[28], k[29], k[30], k[31] ]
        }
    }
    fn as_slice<T, F: Fn(&[u8]) -> T>(&self, f: F) -> T {
        f(&self.bits)
    }
}

I'd like to know if there's a proper way, like k.to_array(32) or something along those lines.

And, yes, I realize the above code could fail with out-of-bounds access. I'm not sure what db_key::Key expects on invalid input.

Edit:

Is there a good way to convert a Vec to an array? is similar but less general. A good answer to this will probably also be a good answer to that question with the addition of taking a slice from the vec, which can be done efficiently and concisely. I also don't consider "write a separate conversion function for each size you care about" to be a proper solution.

How to get a slice as a static array in rust? is also similar, but the accepted answer is the hack I had already come up with independently.

Community
  • 1
  • 1
aij
  • 5,903
  • 3
  • 37
  • 41

1 Answers1

2

You can use a loop to solve it the straightforward (but maybe disappointing) way:

let input = b"abcdef";
let mut array = [0u8; 32];
for (x, y) in input.iter().zip(array.iter_mut()) {
    *y = *x;
}

We can use a function to do a runtime size check and turn a slice into a reference to a fixed size array.

Libstd doesn't provide enough traits to reliably check that the input and output types match here, but we could in theory develop that ourselves (for a finite number of array types). Either way, the cast looks like this, U is arbitrary array type you specify.

/// Return a reference to a fixed size array from a slice.
///
/// Return **Some(array)** if the dimensions match, **None** otherwise.
///
/// **Note:** Unsafe because we can't check if the **U** type is really an array.
pub unsafe fn as_array<T, U>(xs: &[T]) -> Option<&U> where
    U: AsRef<[T]>,
{
    let sz = std::mem::size_of::<U>();
    let input_sz = xs.len() * std::mem::size_of::<T>();

    // The size check could be relaxed to sz <= input_sz
    if sz == input_sz {
        Some(&*(xs.as_ptr() as *const U))
    } else {
        None
    }
}
bluss
  • 12,472
  • 1
  • 49
  • 48
  • Could that be made safe using a macro instead? – aij Apr 12 '15 at 16:06
  • I would prefer to just implement the necessary marker traits on all small array sizes and use that to remove the unsafe. I guess it can be made safe with a macro. – bluss Apr 12 '15 at 21:16