Passing a fixed length slice to func expecting an arbitrary slice

So I have a function where I need to accept arbitrary sized U8 slices, but rust will not let me. How can I fix this?
note: I am in an embedded env, I cannot use allocation, I cannot use std, I cannot use vectors, lists, etc - they require allocation.
It really needs to be done with slices passed by reference

The actual application is HUGE to large to put here.

The small example that i can create that is small and to the point
accepts:

(A) A binary input slice, and
(B) produces an ASCII output slice.

fn  bin2ascii( bin_data_input : &[u8], ascii_data_output : &[u8] )
{
   /* I really want this to be generic for all cases.
    * I do not want "FIXED SIZED" slices.
    * The actual function in the larger context creates packet..
    * But my simple example produces ascii hex in the out buffer.
    */
}

/* I have several hundred "msg functions" like this.
 * Each msg is a different length in ascii.
 *
 * An other example might be creating a UDP network msg.
 * The OUTPUT buffer must always be large enough to hold the msg.
 */
fn sendMsgA( cmd_packet : &[u8:75] )
{
    // Sort of dumb cause the next function effectively initializes this.
     let ascii_bytes : [u8 ; MSG_A_LEN ] = [0;MSG_A_LEN]

     // ERROR[E0053]: See below, second example
     send_msg( ascii_bytes, MSG_A_LEN );
}

fn sendMsgB( cmd_packet : &[u8:32] )
{
     let ascii_bytes : [u8 ; MSG_B_LEN ] = [0;MSG_B_LEN]

     // ERROR[E0053]:  ascii_bytes
     //  expected an array with a size of 64, found one with a size of 129
     bin2ascii( cmd_packet, ascii_bytes );
     send_msg( ascii_bytes, MSG_B_LEN );
}

other things I have considered:

A) using one common buffer size( the largest of them all) presents other problems - It is embedded, so I need to control stack allocation and not be stupid about it, so creating one giant output buffer that is massive is a no go.

B) Or I have a UNIQUE bin2ascii function for every possible size/combination - that seems to be insane

What is necessary is the buffer is large enough for the message.
It is sufficient if the buffer is LARGER then the message

How can I pass an 'large enough buffer' to the common function that formats the data?

You're dealing with the difference between arrays and slices, or references to such. Slices don't have a fixed size, but arrays do.

 [u8; 10]  // an array of length 10
 [u8]      // a slice with a dynamic length (it does not implement Sized)
&[u8; 10]  // a reference to an array of length 10
&[u8]      // a reference to a slice, often *also* called a slice (it is Sized)

Rust arrays don't decay to pointers, like you may be use to from C. But arrays coerce to slices behind a reference, e.g. &[u8; _] coerces to &[u8].

So just pass the arrays by reference.

    let ascii_bytes: [u8; MSG_B_LEN] = [0; MSG_B_LEN];
    bin2ascii(cmd_packet, &ascii_bytes);
    //                    ^
    // Or more explicitly: &ascii_bytes[..], ascii_bytes.as_slice(), etc
4 Likes

Thanks.. Rust is frustrating at times.

Side note.

As quinedot said you can go from a reference to an array to a slice using normal coercion. But if you want to go the other way, and get reference to an array of given length from slice (which has exactly the required length) you can use TryFrom:

struct Packet<'a, const N: usize> {
    data: &'a [u8; N]
}

/// Extracts first 32 bytes from buffer and constructs a Packet.
fn get_first_packet(buffer: &[u8]) -> Packet<'_, 32> {
    // type annotations for clarity
    let data: &[u8] = &buffer[..32];
    let data: &[u8; 32] = data.try_into().expect("cannot fail, silce has required length");
    Packet { data }
}

I'm not sure how Rust is frustrating when you apparently just had a wrong or confused mental model of an extremely fundamental Rust feature. There are no "fixed size slices", slices have a runtime size by definition. I'm also not sure how you managed to get expected an array with a size of 64, found one with a size of 129, because if you're trying to pass an array by value to a function that expects a slice, you get something like

error[E0308]: mismatched types
  --> src/lib.rs:31:29
   |
31 |      bin2ascii( cmd_packet, ascii_bytes );
   |      ---------              ^^^^^^^^^^^ expected `&[u8]`, found `[u8; 32]`
   |      |
   |      arguments to this function are incorrect
   |
note: function defined here
  --> src/lib.rs:1:5
   |
 1 | fn  bin2ascii( bin_data_input : &[u8], ascii_data_output : &[u8] )
   |     ^^^^^^^^^                          -------------------------
help: consider borrowing here
   |
31 |      bin2ascii( cmd_packet, &ascii_bytes );
   |                             +

which very clearly tells you that you have to pass a reference to a function that expects a reference.

3 Likes