如何防止破坏 Rust 中对局部变量的引用?

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

我有两个结构。第一个是 Point 和两个 i32 坐标,第二个是 Line 引用两个 Point。结构有 newrandom 构造函数。

要求的用法是:


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

和结构:

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

出现两个错误。第一个与传递给 LinePoint::newLine::new 执行后被销毁有关,因此无法进一步使用。可以单独取出来单独变量,但是不符合使用要求

第二个错误与构建Line::random所需生成的Point::random是本地的有关,也就是说Line::random执行后,也无法访问。

一个可能的解决方案是使用堆(Box<T>),但我一直无法弄清楚如何在函数完成后避免破坏。

好吧,看来您需要有时引用有时拥有这些点。 Rust 提供 Cow 在这种情况下派上用场:

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.

不是,Box 仍然受限于 rust 借用规则,除非你泄漏它(进行引用 &'static),否则会抱怨在函数作用域后丢弃的临时值。

关于main,你只需要将Points绑定到一个变量,这样它们就会在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

我认为您在这里对引用的使用不当。

如果Line必须有引用,我会使用引用计数的智能指针。

主要问题是,如果您在 Line 中存储引用,Line 不会 拥有 Point。这意味着,你必须让它们在外部保持活力。

这就是您的 random 构造函数失败的原因:

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

由于 Point::new 不拥有 xy,变量 xyrandom函数。


解决方案

有两种方法可以解决这个问题:

  • 使用值而不是引用(例如,使 Point 可克隆)
  • 使用引用计数智能指针

在你的例子中,由于 Point 是一个非常微不足道的类型,我会选择第一个选项:

use sandbox::{Line, Point};

fn main() {
    let line = Line::new(Point::new(1, 2), Point::new(1, 2));
    println!("{:?}", line);
    let line2 = Line::random(10, 10);
    println!("{:?}", line2);
}
use rand::Rng;

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

#[derive(Clone, Debug)]
pub struct Line {
    pub from: Point,
    pub to: 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 {
    pub fn new(from: Point, to: Point) -> Line {
        Line { from, to }
    }
    pub fn random(img_width: i32, img_height: i32) -> Line {
        let a = Point::random(img_width, img_height);
        let b = Point::random(img_width, img_height);
        Line::new(a, b)
    }
}

输出:

Line { from: Point { x: 1, y: 2 }, to: Point { x: 1, y: 2 } }
Line { from: Point { x: 9, y: 1 }, to: Point { x: 9, y: 1 } }

解决方案 #2(带引用计数器)

此方案仅供参考

如前所述,对于简单的数据结构来说,这太过分了,它应该派生出 Clone 特征。

如果您处于 multi-threaded 环境中,请将 Rc<RefCell<Point>> 替换为 Arc<Mutex<Point>>

use std::{cell::RefCell, rc::Rc};

use sandbox::{Line, Point};

fn main() {
    let line = Line::new(
        Rc::new(RefCell::new(Point::new(1, 2))),
        Rc::new(RefCell::new(Point::new(1, 2))),
    );
    println!("{:?}", line);
    let line2 = Line::random(10, 10);
    println!("{:?}", line2);
}
use std::{cell::RefCell, rc::Rc};

use rand::Rng;

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

#[derive(Debug)]
pub struct Line {
    pub from: Rc<RefCell<Point>>,
    pub to: Rc<RefCell<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 {
    pub fn new(from: Rc<RefCell<Point>>, to: Rc<RefCell<Point>>) -> Line {
        Line { from, to }
    }
    pub fn random(img_width: i32, img_height: i32) -> Line {
        let a = Rc::new(RefCell::new(Point::random(img_width, img_height)));
        let b = Rc::new(RefCell::new(Point::random(img_width, img_height)));
        Line::new(a, b)
    }
}

输出:

Line { from: RefCell { value: Point { x: 1, y: 2 } }, to: RefCell { value: Point { x: 1, y: 2 } } }
Line { from: RefCell { value: Point { x: 9, y: 1 } }, to: RefCell { value: Point { x: 4, y: 8 } } }