A small nonsense function (minified from my actual problem): keep the even numbers in the first vector and the odds in the second.
For whatever reason, we don't want to do this inplace. The function uses the tmp Vec for this and for some reason, it stores references to the elements instead copying the values.
However, one optimization is desired: the allocation for tmp should be reused for the second input.
The code below does not pass the borrowchecker and I know why (to the checker, tmp is not guaranteed to be empty and would contain empty references). I could do tmp = Vec::new() but this would give me an empty vector.
Is there some way that I can use the same allocation for the second operation?
The problem is minified from the original problem. Point is: I have a tmp-vec that contains borrowed items and I would like to reuse its allocation to store references to another location after the first computation is finished
I don't know if this would work in your real code, but your minimized version will compile if you re-order it to mutate the inputs after you are done iterating through both of them:
I was thinking about something very similar. A lifetime-extension on an empty-vec-of-references would do the trick.
Is there something in the stdlib or any other major lib that I could use instead of writing unsafe code directly on my own?
This is also unsafe, but instead of messing with lifetimes, you just copy through pointers. I put it into a function which I'm not sure if thats desirable or not. I tried to make it generic but its sloppy because I don't know well enough how to make generics work with function passing.
It is a transmute between the same type [edit: with different lifetimes]. (Different lifetimes don't change [edit: the memory layout of a] a type).
You could also call into_raw_parts() and from_raw_parts() if you want to make 200% sure.
I’m not sure this is an accurate statement. Different lifetimes don’t change the memory layout, but changing a lifetime with transmute is (in general) an unsafe operation. This compiles just fine and won’t immediately crash your program when called, but is still a really bad idea:
// DON’T DO THIS; WILDLY UNSAFE
fn make_static<'a, T>(x: &'a T)->&'static T {
unsafe { std::mem::transmute(x) }
}
Aside
@s3bk: Looking back at your original solution, you obviously understad this; I wrote this more to defend against others misinterpreting your statement.
(A slight aside) I have gained a preference for making unsafe blocks "self-contained". In this case, the transmute is (asserted as) safe because of the clear() but, since the clear() isn't in the unsafe block, it might get deleted/moved/copied elsewhere which would still compile but would now be wildly unsound. Instead I would write this as:
Since then (hopefully) no-one would then consider those two lines as independent. I've noticed quite a few libraries falling foul of this, and I've recently been bitten at work by someone copying one unsafe block from a function when its safety depended on a second unsafe block earlier that they hadn't copied... took a while to track that down
Whether this function is actually UB or not, I'll leave to someone else's better judgement...
Yes, as far as I can see, the reuse_vec function is perfectly safe to use, and does not need to be marked unsafe. The return type being a vector is part of the signature, so callers cannot make it be something else.
Exactly what @alice said, the function signature being written in safe rust means that the function takes a valid/safeVec of references and returns a valid/safeVec of references. The inner unsafely doesn't leak past the assumption that the input was valid.
Yes, you could create a Vec with some other unsafe code (e.g. let v = from_raw_parts(std::ptr::null(), 10, 10)) but that's unsafe and you can't (read, shouldn't) leave unsafe blocks without ensuring all live objects are safe. It's UB to call this function with an invalid Vec, in the same way that it's UB to call any other function on an invalid anything.