5

I have a function doing that for a int:

fn some_to_value(src: Option<&int>) -> Option<int> {
    match src {
        Some(x) => Some(*x),
        None => None
    }
}

and I wanted to make it generic (and still use it with an "int" at the level of the caller). It would be ok for me if the instance of T is copied. So I tried with that:

fn some_to_value<T>(src: Option<&T>) -> Option<T> {
    match src {
        Some(x) => Some(*x),
        None => None
    }
}

I get:

error: cannot move out of dereference of `&`-pointer
Some(x) => Some(*x),
                ^~

I fail to understand why it fails (I'm a beginner).

Some context: I made a copy of an Option, because I realized that after doing a "find" on a "HashMap", the map will be immutable as long as the return of the "find" (an Option containing a reference to an item of the map) is alive.

Fabimaru
  • 423
  • 3
  • 9

4 Answers4

4

The Rust language has a strong concept of ownership, which results in this "move vs. copy" scenario (I'm going to be using terminology from that answer in this one).

A shared reference &T is (normally) a read-only view of a T somewhere in memory. One important property is you're not allowed to invalidate that T: a &T must always point to a valid T instance.

When you write *x you're trying to move the T out by-value, since T is an unbounded generic the compiler has to assume the worst: the type T is not Copy and so must move ownership, that is, a by-value use (aka byte copy) is not a semantic copy, meaning the source cannot continue to be used (see my linked answer for more explanation of this). Moving ownership is invalidating the source... but the source is inside a &T, so invalidating it is illegal!

*x works for int because it is Copy, so a by-value use (aka byte copy) is the same as a semantic copy: the &int is not being invalidated.

Community
  • 1
  • 1
huon
  • 94,605
  • 21
  • 231
  • 225
  • Thank you, now I understand the «move out» part of the error message. As a beginner, I expected to have an error related to a copy, that's why I get confused. – Fabimaru Jun 28 '14 at 08:07
3

If you want to allow T to be copied you have to tell the compiler that.

You can limit the types of T to those of kind Copy:

Types that can be copied by simply copying bits (i.e. memcpy).

fn some_to_value<T: Copy>(src: Option<&T>) -> Option<T> {
    match src {
        Some(x) => Some(*x),
        None => None
    }
}

Or more generically limit the T to types implementing the Clone trait:

fn some_to_value<T: Clone>(src: Option<&T>) -> Option<T> {
    match src {
        Some(x) => Some(x.clone()),
        None => None
    }
}
Arjan
  • 19,957
  • 2
  • 55
  • 48
  • 2
    Incidentally, the first can be written without the whole function as `.map(|x| *x)` or `.map(|&x| x)` and the second `.map(|x| x.clone())`. – Chris Morgan Jun 28 '14 at 00:25
  • Thanks, using directly "map" allows in a concise way to get rid of the Option returned by `find` and making the hashmap immutable. – Fabimaru Jun 28 '14 at 08:37
2

Rust 1.0.0 introduced Option<&T>::cloned() exactly for this (you can get rid of utility wrappers like some_to_value). Excerpt from documentation:

Maps an Option<&T> to an Option<T> by cloning the contents of the option.

Rust 1.26 also introduced an Option<&mut T>::cloned().

Rust 1.35.0 introduced Option<&T>::copied() for restricting it to only-copyable types; see blog post announcing 1.35 on this.

legends2k
  • 31,634
  • 25
  • 118
  • 222
1
Some(x) => Some(*x)

This code takes the x by value. In your first function, the x is an int. Primitives like ints implement the Copy trait, which means that they are automatically copied when taken by value. Types that don't implement Copy are moved instead.

Your generic function does not guarantee that T will implement Copy. If the compiler let this compile, you would end up with a function that dereferences the x, and then either copies or moves the underlying data into the output Option. A move would invalidate the original memory location though!

Your generic function will work with Copy constraint: some_to_value<T: Copy> which guarantees that T will implement Copy.

You probably want the function to work with more than just primitives, so you'll have to use the Clone trait. The match statement can also be written as a map function which does exactly the same.

fn some_to_value<T: Clone>(src: Option<&T>) -> Option<T> {
    src.map(|num| num.clone())
}
A.B.
  • 15,364
  • 3
  • 61
  • 64