繁体   English   中英

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

[英]How to prevent destruction of references to local variables in Rust?

我有两个结构。 首先是带有两个i32坐标的Point ,第二个是带有两个Point引用的Line 结构有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 random 是本地的有关,这意味着Line::random执行后也无法访问。

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

我认为您对引用的使用在这里放错了地方。

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

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

这就是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特征。

如果您处于多线程环境中,请将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 } } }

好吧,这看起来像您需要有时引用和有时拥有这些点。 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)
        }
    }
}

操场

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

并非如此, Box仍然受制于 rust 借用规则,除非您泄漏它(使引用&'static ),否则会抱怨在函数范围之后丢弃的临时值。

关于main ,您只需要将Point s 绑定到一个变量,这样它们就会在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);
}

操场

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM