1

I fail to understand what the problem in the following code is supposed to be:

extern crate rand;
use rand::*;

#[derive(Debug)]
enum Foo {
    A,
    B,
}

static FOOS: [Foo; 2] = [Foo::A, Foo::B];

fn random_foo() -> Foo {
    let i = rand::thread_rng().gen_range(0, FOOS.len());
    FOOS[i]
}

fn main() {
    println!(
        "First: {:?} Second: {:?} Random: {:?}",
        FOOS[0],
        FOOS[1],
        random_foo()
    );
}

I get the error:

error[E0508]: cannot move out of type `[Foo; 2]`, a non-copy array
  --> src/main.rs:14:5
   |
14 |     FOOS[i]
   |     ^^^^^^^ cannot move out of here

Using only the first 2 parts of the println!() and removing fn random_foo(), the code compiles. I cannot see what random_foo() does differently which is worth a compiler error. It only accesses an element of FOOS and tries to return the value, just like the arguments in the print statement in main() do.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
BitTickler
  • 10,905
  • 5
  • 32
  • 53

1 Answers1

6

In your example you're trying to return a value. Once you return FOOS[1], the value is copied to be returned, but in your case your struct can't be copied.

The easiest way is to use references:

extern crate rand;
use rand::*;

#[derive(Debug)]
enum Foo {
    A,
    B,
}
//Your array still uses two values of type Foo
static FOOS: [Foo; 2] = [Foo::A, Foo::B];

/*
 * random_foo() now returns a reference. So the value in FOOS
 * is no longer borrowed.
 */
fn random_foo() -> &'static Foo {
    let i = rand::thread_rng().gen_range(0, FOOS.len());
    &FOOS[i]
}

fn main() {
    println!(
        "First: {:?} Second: {:?} Random: {:?}",
        FOOS[0],
        FOOS[1],
        random_foo()
    );
}

See the Rust book about ownership and borrowing.

See the Rust book about static lifetime used in the example.

The println! macro can use your variables without taking ownership nor copying, it uses them as references. Macros are not functions, they could be compared to C++ macros on this point. They are replaced by the corresponding code before compiling. In a macro, it's possible to use the address of operator for example. See Does println! borrow or own the variable? for more information.

If you really want to use a value instead of the reference, it's possible but you'll have to derive the traits Copy and Clone for your struct. With that done, you can copy the value and return it.

The difference is in your struct:

#[derive(Debug, Copy, Clone)]
enum Foo {
    A,
    B,
}

Please see Move vs Copy in Rust for further information about move and copy.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Anthony
  • 2,014
  • 2
  • 19
  • 29
  • I tried this version I think - but in the context I use this, I really want a value, not a reference. I tried to write stuff like ``&FOOS[i].clone()`` but that also did not really work out... In c++, the difference is obvious: ``const Foo& v = FOOS[0];`` vs ``Foo v = FOOS[0];`` Here in Rust, it looks a bit over-done somehow. Why would I want to move a value out of a constant array? – BitTickler Dec 31 '17 at 00:38
  • @BitTickler I've added an example about how to use your code with the values instead of references. – Anthony Dec 31 '17 at 00:48
  • Perhaps you could add a word or two in your answer about `println!` not being a function; the passed values are taken by reference by default (which is never happening when passing arguments to a function). – Stefan Dec 31 '17 at 09:45
  • After waiting for a few hours to see if something easier shows up, I accepted the answer and...ported my few hundred lines back to C++. Too curious to see the results of my idea. My current verdict on Rust: They really should have value types which spare people all that cryptic jazz. Also they should split CStyle enums (as value types) from discrimnated unions. – BitTickler Dec 31 '17 at 10:08
  • @Stefan I've addded a little word and a link to the println! macro, you're right it can be weird at the beginning. BitTickler, I agree Rust is really different from C++, their philosophy is not the same at all. It's hard at the beginning but in fact Rust is explicit. Also, types like integers that are allocated on the stack are copied by default. But struct are usually on the heap because we don't know their size at compile time, so it's interesting to prevent to copy them by error. In Rust we must derive the Copy trait in order to copy all the value. Sorry to not helping you more. – Anthony Dec 31 '17 at 13:21
  • 4
    The size of a struct is usually known at compile time. But a struct is an abstraction, and in order to provide stable APIs rust only derives very very basic interfaces (`Sync`, `Send`) so you can extend the fields without breaking the API. If you want `Clone` or `Copy` simply `#[derive(...)]` them. Yes, rust is often more explicit than the magic in C++, and I consider that an important feature most of the time. – Stefan Dec 31 '17 at 13:41
  • @Stefan The root problem is the unfortunate design decision to toss enums (a bunch of constants) and discriminated unions (aka sum types) into the same basket. Adding to that is, that `random_foo() -> Foo` returns a value, not a reference. So, the compiler has all the information required to see, it is about a copy of a value, not a reference. So, maybe the compiler could use type inference at least to give less cryptic error messages in such cases. – BitTickler May 12 '22 at 13:03
  • @BitTickler I think this is getting a bit religious (I think that was a quite fortunate decision). I think the error message wasn't too bad (you really should know about `Copy` and understand `non-copy`), and current playground even says: "move occurs because `FOOS[_]` has type `Foo`, which does not implement the `Copy` trait" - so you know you need to learn about the `Copy` trait if you don't get it. (Actually I'd prefer `Sync` and `Send` to be explicit too; it's too easy to break the API of a crate with auto-implemented traits.) – Stefan May 17 '22 at 22:30
  • @Stefan IMHO, it is about usability vs being a pain in the rear. I ran into this the first time when I tried to create an enum of square names of a board game and wanted to use those in a statically computed lookup table of such a game (was chess). Sometimes you really just want a plain symbol for an (integral) value and then everything gets more unwieldy because that use case was mixed in with a totally disjoint one (discriminated unions). – BitTickler May 17 '22 at 22:40