Return owned value and reference to value

ghz 8months ago ⋅ 120 views

I have a Rust function, that has a fixed input type, but I can choose the return type:

fn foo(data: &[u8])

This function needs to make changes to that slice, so I created an owned value, so a Vec<u8>. I have to extract some parts of this vector into a library defined type, one which looks like this:

struct Foo<'a> {
    part1: &'a u8,
    part1: &'a u8,
    part1: &'a u8,
}

The struct does not have an owned type, and its internal types are all lifetimed. This struct can be created with a function that takes has his signature:

fn create(&'a [u8]) -> Foo<'a>

How can I return such a type? I can't just return it, as it references a local variable (the vector), which would be dropped at the end of the function. I also can't just return the both the vector and the Foo struct, as that would move the vector, and invalidate the reference to it.

If it is any help, the struct is a X509CertificationRequest from the crate x509_parser.

I have tried to convert the vector to a Pin<Box<Vec<u8>>> and borrow the data from that and return both of them, but that did not work. How would one do that? I have read this, and this question, but I have found them to be unhelpful to my case, as they all suggest to rewrite the struct, which I cannot do.

Answers

In Rust, you can't directly return a reference to data that is owned by the function. However, you can return an owned value that contains references to the data. One way to achieve this is by using a combination of Vec<u8> and the Foo<'a> struct, where Foo<'a> contains references to the Vec<u8>.

Here's how you can do it:

struct Foo<'a> {
    part1: &'a u8,
    part2: &'a u8,
    part3: &'a u8,
}

fn create(data: &[u8]) -> Foo {
    // Ensure the data slice has at least three elements
    assert!(data.len() >= 3);

    // Create a Foo struct containing references to the first three elements of the slice
    Foo {
        part1: &data[0],
        part2: &data[1],
        part3: &data[2],
    }
}

fn foo(data: &[u8]) -> (Vec<u8>, Foo) {
    // Make changes to the data slice
    // For demonstration, let's create a Vec<u8> and fill it with some modified data
    let mut modified_data = Vec::new();
    for &byte in data {
        // Modify each byte, for example, increment it by 1
        modified_data.push(byte + 1);
    }

    // Create a Foo struct using the modified data
    let foo = create(&modified_data);

    // Return the modified data and the Foo struct
    (modified_data, foo)
}

fn main() {
    let data = [1, 2, 3, 4, 5];
    let (modified_data, foo) = foo(&data);

    // Print the modified data and the parts of the Foo struct
    println!("Modified Data: {:?}", modified_data);
    println!("Foo: part1={}, part2={}, part3={}", foo.part1, foo.part2, foo.part3);
}

In this example:

  • The create function creates a Foo struct containing references to the first three elements of the input slice.
  • The foo function modifies the input slice and then creates a Foo struct using the modified data.
  • It returns a tuple containing the modified data and the Foo struct.
  • In the main function, you can see how to use the returned values.