如何防止破坏 Rust 中对局部变量的引用?
How to prevent destruction of references to local variables in Rust?
我有两个结构。第一个是 Point
和两个 i32
坐标,第二个是 Line
引用两个 Point
。结构有 new
和 random
构造函数。
要求的用法是:
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
}
}
出现两个错误。第一个与传递给 Line
的 Point::new
在 Line::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)
}
}
}
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
,你只需要将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);
}
我认为您在这里对引用的使用不当。
如果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
不拥有 x
和 y
,变量 x
和 y
在 random
函数。
解决方案
有两种方法可以解决这个问题:
- 使用值而不是引用(例如,使
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 } } }
我有两个结构。第一个是 Point
和两个 i32
坐标,第二个是 Line
引用两个 Point
。结构有 new
和 random
构造函数。
要求的用法是:
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
}
}
出现两个错误。第一个与传递给 Line
的 Point::new
在 Line::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)
}
}
}
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
,你只需要将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);
}
我认为您在这里对引用的使用不当。
如果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
不拥有 x
和 y
,变量 x
和 y
在 random
函数。
解决方案
有两种方法可以解决这个问题:
- 使用值而不是引用(例如,使
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 } } }