Returning data which contains data owned by function

I want to have a DataAllocator that can return data with lifetime 'r, so I can pass it to something and still be able to return the value allocated inside something:

use std::marker::PhantomData;

pub struct Data<'a, T>{
    a: &'a [T]
}

impl<'a, T> Data<'a, T> {
    pub fn new() -> Data<'a, T> {
        todo!()
    }
    
    fn iter(&'a self) -> Iter<'a, T> {
        todo!()
    }
}

pub struct Iter<'a, T> {
    _phantom: PhantomData<&'a T>
}

impl<'a, T> Iterator for Iter<'a, T> {
	type Item = &'a T;
	fn next(&mut self) -> Option<Self::Item> {
		todo!()
	}
}

pub trait DataAllocator<'s, 'r, T> {
    fn allocate(&'s self) -> Box<dyn AsRef<[T]> + 'r>;
}

impl<'s, 'r, T> DataAllocator<'s, 'r, T> for Data<'r, T> {
    fn allocate(&'s self) -> Box<dyn AsRef<[T]> + 'r> {
        todo!()
    }
}

fn do_something<'a, 'b, T>(data_allocator: &'a dyn DataAllocator<'a, 'b, T>) -> Data<'b, T> {
    let buffer = data_allocator.allocate();
    let data = Data{a: (*buffer).as_ref()};
    for e in data.iter(){}
    data
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2a3e0d0cdea86238413af25dddd04e19

error[E0515]: cannot return value referencing local data `*buffer`
  --> src/lib.rs:42:5
   |
40 |     let data = Data{a: (*buffer).as_ref()};
   |                        ------------------ `*buffer` is borrowed here
41 |     for e in data.iter(){}
42 |     data
   |     ^^^^ returns a value referencing data owned by the current function

The problem is that if I try to iterate over data, then it complains that I'm trying to return something that is borrowed by the .iter() function. I think it might have to do with me saying that .iter() borrows the Data for the lifetime 'b, which is the entire lifetime of the allocated data. What can I do in this case?

Data needs to take ownership of the buffer.

1 Like

yes, but in this case I don't want this, I want a buffer that has 'b lifetime

Your trait should look something like this:

pub trait DataAllocator<'a, T> {
    fn allocate(&self) -> &'a mut [T];
}

The Box<dyn AsRef<[T]>> is needed because I want to return not only slices.

Also, what if I want an allocator that returns a data with a different lifetime? I don't see why -> Box<dyn AsRef<[T]> + 'r> is bad.

Remember that the data allocator can have its own lifetime, but I want to create, inside do_something, a data with a buffer allocated from data_allocator.

A box is not going to work. It's an owned type even if you put a lifetime on it

yes, I want it to be owned, I just use box so the return type is Sized and the thing that can execute AsRef is there, owned. But I'm ok with a box that lives shortly

The problem is that AsRef gives you a reference to the allocation owned by the box, and that allocation is not tied to the lifetime on the inner type, and cannot live past when the box goes out of scope.

I think this is what I wanted, actually. I do not intend to make the thing inside the box live by itself. The Box is just for storing the dyn. Why I do this? Well, some rust memory allocators like TypedArena give you references anyway, so I just wanted a common interface for using different memory allocators. Returning -> Box<dyn AsRef<[T]> + 'r>; gives the flexibility that I wanted.

I just don't understand why I'm getting that error.

Maybe something like Rust Playground

As for why you're getting the error, it boils down to constructing a box that gets dropped at the end of the scope but you still want to have its reference which just cant work.

It's because you're trying to return a reference into the box' allocation, which is destroyed when the box goes out of scope.

1 Like

The problem with your solution is that it only lets me return references. What if I want to really allocate something that lives by itself? For example, I could use TypedArena for allocating a true slice, but I could also want to use an inneficient allocator that just alocates a Vec, which lives by itself. That's why I wanted the Box to contain anything that implement AsRef<[T]>, not just return a slice. Do you think it's possible?

in summary: I want to return a Box of something that implements AsRef<[T]>, but this something could be a slice (short lifetime) or a vec (static lifetime). The second case is for when I need to pass things to other threads, etc. And the lifetime, of course, should be the 'r from DataAllocator

Perhaps you want Cow<'a, [T]>?

wouldn't it clone the slice allocated by TypedArena, everytime I need to mutate it? (I need to mutate in most of the cases). I cannot afford these clones, that's why I'm using a memory pool. But for some cases I want to relax and use a Vec

If you have a Cow::Borrowed(...), calling Cow::to_mut will turn it into a Cow::Owned(...), allocating a new Vec in the process. After that, if you call Cow::to_mut again on the same value, it'll just give you a mutable reference to the existing Vec, rather than allocating again.

Then what magic pixie dust compiler is supposed to use to distinguish these cases?

Remember that lifetimes is compile-time construct, they don't exist during program execution.

it was meant to be used at compile time. When I know it's a threaded thing, I use the 'static version with vec allocation. When I'm gonna use the type inside the same thread, I use the lifetimed

Then the returned type have to describe these two cases, somehow. Because not only you need to know which version was choosen, compiler needs to know, too.

If Cow is not suitable then you would have to create your own type with appropriate semantic.

But if you return some type which couldn't carry the required information (like Box) compiler would be confused.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.