2

I'm trying to write a function with this signature, without using clone() or allocating a new vector:

fn mapVec(f: Fn(T) -> T, x: Vec<T>) -> Vec<T>

In my head, this seems to be doable. For each element in x, we pass it to f, with f taking ownership of it. We then produce a return value, and then as f has taken ownership this passed value, it destroys it if necessary. We then place the return value in place back into the vector. As we've taken ownership of x, we're allowed to modify it, and then we can return it to the caller.

My attempt was the following:

for e in x.iter_mut() {
    *e = f(e);
}
return x;

But unfortunately f expects a T, not a &mut T.

I don't want to change the signature of mapVec to for example, use mutable functions, I want this to look like a pure function from the outside if possible, just taking advantage of mutation on the inside because we can get away with it because the caller has passed us ownership of the object.

hellow
  • 12,430
  • 7
  • 56
  • 79
Clinton
  • 22,361
  • 15
  • 67
  • 163
  • How about using into_iter then collect the results into another vec ? – Ömer Erden Jun 03 '19 at 05:41
  • I've edited the question but no I want to avoid the new allocation as the return type is the same and I've taken ownership I want to avoid the new allocation – Clinton Jun 03 '19 at 05:43
  • 2
    Please note that `return x` as last statement is not concidered as good rust style. See https://stackoverflow.com/q/27961879 – hellow Jun 03 '19 at 06:05
  • Why do you suppose it will do a new allocation ? Compiler could be smart to avoid it. Anyway I don't think it will cost must. – Stargateur Jun 03 '19 at 08:03

2 Answers2

5

You are essentially encountering the problem the replace_with and take_mut crates are trying to solve. Simply putting usage of one of those crates into the for-loop would solve your issue.

Be aware of the caveats though. If the map callback panics, bad things happen. This is because what you want to do is move the object out of its location, call a function, then put the result back into the location. But if the function doesn't produce a result, i.e. panics, what do you put in the empty location?

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • Those complications can be avoided by swapping in a placeholder while temporarily taking out the value – the8472 Jun 03 '19 at 11:12
  • For that you either need a placeholder supplied (doesn't even need to be `Clone`, you can reuse the same placeholder), or a type that is `Default`. – Sebastian Redl Jun 03 '19 at 12:33
  • I'd recommend adding your answer to the duplicate; as it appears to not be present there. – Shepmaster Jun 03 '19 at 15:19
0

The only difference in this working example is that the callback takes a reference to T and not T itself but still not a mutable one:

fn map_vec<F, T>(f: F, x: Vec<T>) -> Vec<T>
where
    F: Fn(&T) -> T,
{
    let mut x = x;
    for e in x.iter_mut() {
        *e = f(e);
    }
    x
}

fn main() {
    let x = vec![1, 2, 3];
    let y = map_vec(|e| e + 1, x);
    println!("{:?}", y);
}
Peter Varo
  • 11,726
  • 7
  • 55
  • 77