Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 448
Greg Malcolm Avatar asked Jan 23 '14 13:01

Greg Malcolm


2 Answers

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.

like image 115
A.B. Avatar answered Sep 22 '22 08:09

A.B.


=== 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()
}
like image 30
Paolo Falabella Avatar answered Sep 23 '22 08:09

Paolo Falabella