11

I'm a Rust newbie trying to figure out the language by playing with it. I've hit some problems on trying to return an array from a function:

struct Widget {
  thingies: ~[int]
}

impl Widget {
    fn new() -> Widget {
        Widget { thingies: ~[4, 8, 15, 16, 23, 42] }
    }

    fn somethings(&self) -> ~[int] {
        self.thingies
    }
}

fn main() {
   let widget = Widget::new();
   let wotsits = widget.somethings();
}

This of course fails compilation with this error:

pointers.rs:11:8: 11:21 error: cannot move out of dereference of & pointer
pointers.rs:11         self.thingies

In case this code sample looks out of sorts, all I'm trying to do is pull an array out of an implemented struct. The borrowed pointer isn't important, it's just how I'm trying to store the data.

Any tips on how to extract my array properly?

Btw, I'm using Rust 0.8

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Greg Malcolm
  • 3,238
  • 4
  • 23
  • 24
  • `~` is a unique pointer and there can only be one of them (to the same value). They can't be copied (aliased) like raw C pointers. Trying to create another value (the return value) moves the value out of the original pointer, which would make `thingies` and whole `self` undefined, which of course is not allowed. You probably meant to return a borrowed pointer, not to take away `self.thingies`. – hamstergene Jan 23 '14 at 13:22
  • ~[T] is called an *vector*, not an *array*. The distinction people typically draw between the two is that an array is statically sized while a vector can grow or shrink. – Chris Morgan Jan 23 '14 at 14:30

3 Answers3

15

The reason your code doesn't compile is that a unique pointer ~ can have only one owner. The compiler is preventing you from writing error prone code. You can either decide to return a copy of thingies, a reference to thingies, or a slice of thingies (which is a reference to the vector data or a segment of it).

Copy solution

struct Widget {
  thingies: ~[int]
}

impl Widget {
    fn new() -> Widget {
        Widget { thingies: ~[4, 8, 15, 16, 23, 42] }
    }

    fn somethings(&self) -> ~[int] {
        self.thingies.clone()
    }
}

Reference solution

struct Widget {
  thingies: ~[int]
}

impl Widget {
    fn new() -> Widget {
        Widget { thingies: ~[4, 8, 15, 16, 23, 42] }
    }

    fn somethings<'a>(&'a self) -> &'a~[int] {
        &self.thingies
    }
}

Slice solution

struct Widget {
  thingies: ~[int]
}

impl Widget {
    fn new() -> Widget {
        Widget { thingies: ~[4, 8, 15, 16, 23, 42] }
    }

    fn somethings<'a>(&'a self) -> &'a[int] {
        self.thingies.as_slice()
    }
}

To understand the reference and slice solutions you need to understand what 'a means: it indicates a lifetime, and &'a is a way to tell the compiler that the reference must never outlive the object it references, which in this case is a Widget.

These solutions also have some limitations: you cannot modify an object that you're currently referencing because doing so opens up the possibility of the references becoming invalid.

You can of course modify thingies if you return a mutable reference. A mutable reference with a lifetime would be written &'a mut T

struct Widget {
  thingies: ~[int]
}

impl Widget {
    fn new() -> Widget {
        Widget { thingies: ~[4, 8, 15, 16, 23, 42] }
    }

    fn somethings<'a>(&'a mut self) -> &'a mut ~[int] {
        &mut self.thingies
    }
}

Note I believe that in Rust 0.8, you need to write &'self instead of &'a because lifetimes with custom names weren't supported yet. I also wrote this in 0.9.

Edit: removed redundant lifetime declarations.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
A.B.
  • 15,364
  • 3
  • 61
  • 64
  • You do not need to put lifetime annotation on a struct unless you need to have borrowed pointers inside the struct. This is not the case. You can just parameterize `somethings` method: `fn somethings<'a><(&'a mut self) -> &'a mut ~[int] ...`. – Vladimir Matveev Jan 23 '14 at 15:00
  • Regarding the 0.8 comment, I found the code worked as is with &'a. I didn't need to change it to &'self. – Greg Malcolm Feb 06 '14 at 00:43
8

=== EDIT ===

in Rust 1 stable, ~[T] became Vec<T>, but (syntax aside) the same issue applies, as a Vec still has a unique owner. In a nutshell, somethings only has a reference to self and (through the reference) it can't become the owner of thingies. Playground link to Rust 1 version here: https://play.rust-lang.org/?gist=50ec1acdc684e53fd5f9&version=stable.

Rust's ownership model is quite central to the language, so for more info I'd suggest looking at the great official documentation on ownership and borrowing

=== END EDIT ===

In Rust, the . after self, auto-dereferences self, so this is the dereference of & pointer that the error mentions.

Now ownership of thingies is the part that you cannot move out of the dereference:

   let widget = Widget::new(); // widget owns the unique pointer to self
   let wotsits = widget.somethings(); // if this worked, ownership of 
                                      // unique pointer to thingies would be
                                      // moved to wotsits

You could borrow a reference to thingies instead:

fn somethings<'a>(&'a self) -> &'a~[int] {
    &self.thingies
}

or explicitly return a copy of thingies

fn somethings(&self) -> ~[int] {
    self.thingies.clone()
}
Paolo Falabella
  • 24,914
  • 3
  • 72
  • 86
  • Hate to ping you again, but more people are trying to update pre-1.0 questions. Let's have a [bigger group chat](http://chat.stackoverflow.com/rooms/62927/rust). – Shepmaster Nov 11 '15 at 14:14
2

You cannot move out of borrowed pointer, as is explained in other answers. But you can pass self by value or by owned pointer, then you will be able to return owned vector:

struct Widget {
    thingies: ~[int]
}

impl Widget {
    fn somethings(self) -> ~[int] {
        self.thingies
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296