How to prevent destruction of references to local variables in Rust?

Advertisements

I have two structures. First is Point with two i32 coordinates, second is a Line with references to two Points. Structures have new and random constructors.

Required usage is:


use sandbox::{Point, Line};

fn main() {
    let line = Line::new(&Point::new(1, 2),
                         &Point::new(1, 2));
    line.from; // error[E0716]: temporary value dropped while borrowed
    Line::random(10, 10); // error[E0515]: cannot return value referencing local variable `a`
}

And structs:

use rand::Rng;

pub struct Point {
    pub x: i32,
    pub y: i32,
}

pub struct Line<'line> {
    pub from: &'line Point,
    pub to: &'line Point,
}

impl Point {
    pub fn new(x: i32, y: i32) -> Point {
        Point { x, y }
    }
    pub fn random(x_max: i32, y_max: i32) -> Point {
        let x = rand::thread_rng().gen_range(0..=x_max);
        let y = rand::thread_rng().gen_range(0..=y_max);
        return Point::new(x, y);
    }
}

impl<'line> Line<'line> {
    pub fn new<'a>(from: &'a Point, to: &'a Point) -> Line<'a> {
        Line { from, to }
    }
    pub fn random<'a>(img_width: i32, img_height: i32) -> Line<'a> {
        let a = Point::random(img_width, img_height);
        let b = Point::random(img_width, img_height);
        Line::new(&a, &b)
        // error[E0515]: cannot return value referencing local variable `a`
        // returns a value referencing data owned by the current function
    }
}

Two errors occur. The first is related to the fact that the Point::new passed to Line is destroyed after Line::new is executed, so further usage is impossible. It would be possible to take it out into separate variable, but this does not meet the requirements of the usage.

The second error is related to the fact that the generated Point::random needed to build Line::random is local, which means that after Line::random is executed, it also become inaccessible.

One possible solution is to use a heap(Box<T>), but I haven’t been able to figure out how to avoid destruction after the function completes.

>Solution :

Well, this looks like you need for the points to be sometimes referenced and sometimes owned. Rust provides Cow which comes in handy for this cases:

use rand::Rng;
use std::borrow::Cow;

#[derive(Clone)]
pub struct Point {
    pub x: i32,
    pub y: i32,
}

pub struct Line<'line> {
    pub from: Cow<'line, Point>,
    pub to: Cow<'line, Point>,
}

impl Point {
    pub fn new(x: i32, y: i32) -> Point {
        Point { x, y }
    }
    pub fn random(x_max: i32, y_max: i32) -> Point {
        let x = rand::thread_rng().gen_range(0..=x_max);
        let y = rand::thread_rng().gen_range(0..=y_max);
        return Point::new(x, y);
    }
}

impl<'line> Line<'line> {
    pub fn new(from: &'line Point, to: &'line Point) -> Line<'line> {
        Line { from: Cow::Borrowed(from), to:  Cow::Borrowed(to)}
    }
    pub fn random(img_width: i32, img_height: i32) -> Line<'line> {
        let a = Point::random(img_width, img_height);
        let b = Point::random(img_width, img_height);
        Self {
            from: Cow::Owned(a),
            to: Cow::Owned(b)
        }
    }
}

Playground

One possible solution is to use a heap(Box), but I haven’t been able to figure out how to avoid destruction after the function completes.

It does not, Box are still constrain to rust borrowing rules, and unless you leak it (making the references &'static) will complain about the temporary values droped after the function scope.

About the main, you just need to bind the Points to a variable, so they will live for the scope of main:

fn main() {
    let (from, to) = (Point::new(1, 2), Point::new(1, 2));
    let line = Line::new(&from, &to);
    line.from;
    Line::random(10, 10);
}

Playground

Leave a ReplyCancel reply