dandyvica
dandyvica

Reputation: 21

How do I write a function that adds an element to a vector, allowing the element to be changed before insertion?

I'm trying to make a simple example involving a vector of structs to learn Rust. All examples of vectors in the Rust literature I've found only use vectors of integers.

I want to write a function aimed at filling a vector, allowing the possibility of the element to be inserted to be changed, I can't figure out what to do. I always got a compiler error[E0308]: mismatched types on the push method, because elem is a reference to a Point. So

What is the right thing to do?

// structure used everywhere in Rust examples
#[derive(Debug)]
struct Point {
    x: i16,
    y: i16
}

fn add_element(v: &mut Vec<Point>, elem: &Point) {
    // modify element
    elem.x = 0;

    // add element
    v.push(elem);
}

// this example is meant to study a vector of structs
fn main() {
    // declare 2 points. By default, live on the stack
    let origin = Point {x:0, y:0};
    println!("origin address\t: {:p}", &origin);
    let mut p1 = Point {x:1, y:1};
    println!("p1 address\t: {:p}", &p1);    

    // declare a new vector of structs. Allocation is made in the heap
    // declare mutable because we'll add elements to vector
    let mut v: Vec<Point> = Vec::new();

    // add points
    add_element(&mut v, &origin);
    add_element(&mut v, &p1);    

    // change p1
    p1.x = 2;
    p1.y = 2;
}

Upvotes: 2

Views: 2071

Answers (1)

Shepmaster
Shepmaster

Reputation: 430673

Let's read the error messages together:

error[E0308]: mismatched types
  --> src/main.rs:10:12
   |
10 |     v.push(elem);
   |            ^^^^ expected struct `Point`, found &Point
   |
   = note: expected type `Point`
   = note:    found type `&Point`

The code is attempting to store a reference to a Point in a Vec that is declared to hold entire Points. Since Rust is a statically- and strongly- typed language, the compiler tells you that you cannot do that. The fix is to accept a Point by value:

fn add_element(v: &mut Vec<Point>, elem: Point)

This leads to the next error:

error: cannot assign to immutable field `elem.x`
 --> src/main.rs:9:5
  |
9 |     elem.x = 0;
  |     ^^^^^^^^^^

You cannot change members of elem because it is not marked as mutable. Mutability of a value is a property of the binding, so let's do that:

fn add_element(v: &mut Vec<Point>, mut elem: Point)

Then change the calling of that function to adapt:

fn main() {
    let origin = Point { x: 0, y: 0 };
    let p1 = Point { x: 1, y: 1 };

    let mut v = Vec::new();

    add_element(&mut v, origin);
    add_element(&mut v, p1);
}

Note that neither origin nor p1 need to be mutable because this function doesn't modify either while it owns it. It transfers ownership to add_element, which chooses to make it mutable.

but if I want to modify elem, I need to pass a (mutable?) reference

As you can see, you can simply make the elem parameter mutable when transferring the entire value to the function. Since the function owns that value, it has full control over it, including choosing to make it mutable.

Upvotes: 2

Related Questions