Greg Malcolm
Greg Malcolm

Reputation: 3397

How do I return an owned array from a function?

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

Upvotes: 11

Views: 17046

Answers (3)

Paolo Falabella
Paolo Falabella

Reputation: 25844

=== 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()
}

Upvotes: 8

A.B.
A.B.

Reputation: 16640

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.

Upvotes: 15

Vladimir Matveev
Vladimir Matveev

Reputation: 127791

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
    }
}

Upvotes: 2

Related Questions