Search code examples
rustlifetime

How to return a slice for an array created inside a function (in a clean Rust manner)


This is probably a question that has been asked many times, sorry about that.

I want to make a function that creates a byte array and returns a slice to it, and use only lifetimes.

I don't care how the byte array is created but it needs an initial size, and it will be mutated by a function that receives a unsized array. The function should return an unsized array (because in practice I do not know what is the size of the array at the start of the functions

fn create_new_byte_array(v :&[u8]) -> Result<&[u8], io::Error> {
    const N: usize = 1500;
    //let mut result: Vec<u8> = Vec::<_>::with_capacity(N);
    let mut result: [u8; N] = [0; N];
    // builder.write(&mut result.as_mut_slice(), v); // some byte transform
    return Ok(&result.as_slice());
}

How can this be done in a Rust manner? Any tips about other details are welcome for a noob.


Solution

  • The reason you can't generally return a slice referring to data created within a function like this is fundamental and there's no way around it, without allowing a memory leak.

    A slice is just a view into some data stored in memory. It's a reference to an array. But where does the array itself live? If you create it with a Vec, then the array lives on the heap and you need to manage its ownership. When that Vec goes out of scope it will be dropped and its contents are then invalid.

    If you create the array as a local variable in the function, then it's allocated on the stack, and it will cease to exist when the function returns.

    If you want to return a slice referring to data created within the function, then you need to intentionally create a memory leak, but ensuring the referred-to data is never freed once you return the reference.

    fn main() {
        let slice1 = make_slice(55);
        let slice2 = make_slice(66);
        println!("slice 1: {slice1:?}");
        println!("slice 2: {slice2:?}");
        // Note that slice1 and slice2 will never be freed since we called Vec::leak
    }
    
    fn make_slice(n: u8) -> &'static [u8] {
        let v = vec![n; 10];
        
        // The 'static lifetime means the referred-to data needs to be guaranteed to
        // live for the rest of the program's execution.
        v.leak()  // <-- data will NEVER be freed, we return a reference to this data
    }