如何在您创建的数据需要引用的地方创建工厂函数?
How to create a factory function where the data you're creating requires references?
在某些情况下,您希望工厂函数创建一些数据,但出于某种原因,您 return 必须 包含引用的数据。这似乎不可能,因为您不能 return 从函数引用。
让我们看下面的示例代码:
// Important: traits are of unknown size, and so must be passed around
// as a reference
trait Shape {
fn area(&self) -> f32;
}
// As seen here.
struct ShapeCollection<'a> {
shapes: Vec<&'a Shape>
}
// This function needs to return a ShapeCollection, but since the
// data structure requires references to work, it seems impossible!
fn make_shapes<'a>(needRectangle: bool, multiplier: f32) -> ShapeCollection<'a> {
let rect = Rectangle {
width: 42.0 * multiplier,
height: 24.0 * multiplier
};
let circle = Circle {
radius: 42.0 * multiplier
};
match needRectangle {
true => ShapeCollection {
shapes: vec![&rect, &circle]
},
false => ShapeCollection {
shapes: vec![&circle]
},
}
}
// ^ This function won't compile because rect and circle do not
// life long enough, and the compiler dies at this point
// Impls if you're interested / want to compile, but not that important
struct Rectangle {
width: f32,
height: f32
}
impl Shape for Rectangle {
fn area(&self) -> f32 {
self.width * self.height
}
}
struct Circle {
radius: f32
}
impl Shape for Circle {
fn area(&self) -> f32 {
(std::f32::consts::PI * self.radius).powf(2f32)
}
}
这是一个基于我正在编写的更复杂代码构建的简化示例。问题的要点是两个混淆的要求:
- 我需要多态性。因为我为此使用了特征,并且特征没有确定大小,所以
Vec
必须包含引用而不是值
- 我需要一个工厂函数。因为我想从我的代码的多个部分生成这个对象列表但参数不同,所以制作一个封装它的函数是有意义的,而不是复制和粘贴周围的逻辑
如何在 Rust 中解决这两个要求?
两个选择好像是:
- 做一些其他事情来实现多态性,允许作为值传递的东西
- 以不是创建参数化函数的方式删除多个代码块
一种可能性似乎是使用枚举而不是特征来实现多态性。
对于此示例,它将是:
enum Shape2 {
Rectangle { width: f32, height: f32 },
Circle { radius: f32 }
}
fn area(shape: Shape2) -> f32 {
match shape {
Shape2::Rectangle {width, height} => width * height,
Shape2::Circle {radius} => (std::f32::consts::PI * radius).powf(2f32)
}
}
struct Shape2Collection {
shapes: Vec<Shape2>
}
fn make_shapes2(needRectangle: bool, multiplier: f32) -> Shape2Collection {
let rect = Shape2::Rectangle {
width: 42.0 * multiplier,
height: 24.0 * multiplier
};
let circle = Shape2::Circle {
radius: 42.0 * multiplier
};
match needRectangle {
true => Shape2Collection {
shapes: vec![rect, circle]
},
false => Shape2Collection {
shapes: vec![circle]
},
}
}
这样做的缺点是您的实现不再是方法,而是函数,并且您的实现被混杂在同一个函数中。
你只需cannot return a reference to a variable created in a function.
Important: traits are of unknown size, and so must be passed around as a reference
这不完全正确。它们必须通过某种间接方式传递,但引用 (&
) 并不是 trait 对象 可以使用的唯一一种间接方式。还有盒装特征对象:
struct ShapeCollection {
shapes: Vec<Box<Shape>>,
}
fn make_shapes(need_rectangle: bool, multiplier: f32) -> ShapeCollection {
let rect = Rectangle {
width: 42.0 * multiplier,
height: 24.0 * multiplier,
};
let circle = Circle {
radius: 42.0 * multiplier,
};
match need_rectangle {
true => ShapeCollection {
shapes: vec![Box::new(rect), Box::new(circle)],
},
false => ShapeCollection {
shapes: vec![Box::new(circle)],
},
}
}
在 99.9% 的情况下,生命周期仅在 return 位置的函数签名永远无法工作(或者不做你想做的事)并且应该 always给你暂停:
fn make_shapes<'a>(need_rectangle: bool, multiplier: f32) -> ShapeCollection<'a>
请注意,Rust 样式是 snake_case
,因此我重命名为 need_rectangle
。
另请参阅:
- What makes something a "trait object"?
- Is there any way to return a reference to a variable created in a function?
在某些情况下,您希望工厂函数创建一些数据,但出于某种原因,您 return 必须 包含引用的数据。这似乎不可能,因为您不能 return 从函数引用。
让我们看下面的示例代码:
// Important: traits are of unknown size, and so must be passed around
// as a reference
trait Shape {
fn area(&self) -> f32;
}
// As seen here.
struct ShapeCollection<'a> {
shapes: Vec<&'a Shape>
}
// This function needs to return a ShapeCollection, but since the
// data structure requires references to work, it seems impossible!
fn make_shapes<'a>(needRectangle: bool, multiplier: f32) -> ShapeCollection<'a> {
let rect = Rectangle {
width: 42.0 * multiplier,
height: 24.0 * multiplier
};
let circle = Circle {
radius: 42.0 * multiplier
};
match needRectangle {
true => ShapeCollection {
shapes: vec![&rect, &circle]
},
false => ShapeCollection {
shapes: vec![&circle]
},
}
}
// ^ This function won't compile because rect and circle do not
// life long enough, and the compiler dies at this point
// Impls if you're interested / want to compile, but not that important
struct Rectangle {
width: f32,
height: f32
}
impl Shape for Rectangle {
fn area(&self) -> f32 {
self.width * self.height
}
}
struct Circle {
radius: f32
}
impl Shape for Circle {
fn area(&self) -> f32 {
(std::f32::consts::PI * self.radius).powf(2f32)
}
}
这是一个基于我正在编写的更复杂代码构建的简化示例。问题的要点是两个混淆的要求:
- 我需要多态性。因为我为此使用了特征,并且特征没有确定大小,所以
Vec
必须包含引用而不是值 - 我需要一个工厂函数。因为我想从我的代码的多个部分生成这个对象列表但参数不同,所以制作一个封装它的函数是有意义的,而不是复制和粘贴周围的逻辑
如何在 Rust 中解决这两个要求?
两个选择好像是:
- 做一些其他事情来实现多态性,允许作为值传递的东西
- 以不是创建参数化函数的方式删除多个代码块
一种可能性似乎是使用枚举而不是特征来实现多态性。
对于此示例,它将是:
enum Shape2 {
Rectangle { width: f32, height: f32 },
Circle { radius: f32 }
}
fn area(shape: Shape2) -> f32 {
match shape {
Shape2::Rectangle {width, height} => width * height,
Shape2::Circle {radius} => (std::f32::consts::PI * radius).powf(2f32)
}
}
struct Shape2Collection {
shapes: Vec<Shape2>
}
fn make_shapes2(needRectangle: bool, multiplier: f32) -> Shape2Collection {
let rect = Shape2::Rectangle {
width: 42.0 * multiplier,
height: 24.0 * multiplier
};
let circle = Shape2::Circle {
radius: 42.0 * multiplier
};
match needRectangle {
true => Shape2Collection {
shapes: vec![rect, circle]
},
false => Shape2Collection {
shapes: vec![circle]
},
}
}
这样做的缺点是您的实现不再是方法,而是函数,并且您的实现被混杂在同一个函数中。
你只需cannot return a reference to a variable created in a function.
Important: traits are of unknown size, and so must be passed around as a reference
这不完全正确。它们必须通过某种间接方式传递,但引用 (&
) 并不是 trait 对象 可以使用的唯一一种间接方式。还有盒装特征对象:
struct ShapeCollection {
shapes: Vec<Box<Shape>>,
}
fn make_shapes(need_rectangle: bool, multiplier: f32) -> ShapeCollection {
let rect = Rectangle {
width: 42.0 * multiplier,
height: 24.0 * multiplier,
};
let circle = Circle {
radius: 42.0 * multiplier,
};
match need_rectangle {
true => ShapeCollection {
shapes: vec![Box::new(rect), Box::new(circle)],
},
false => ShapeCollection {
shapes: vec![Box::new(circle)],
},
}
}
在 99.9% 的情况下,生命周期仅在 return 位置的函数签名永远无法工作(或者不做你想做的事)并且应该 always给你暂停:
fn make_shapes<'a>(need_rectangle: bool, multiplier: f32) -> ShapeCollection<'a>
请注意,Rust 样式是 snake_case
,因此我重命名为 need_rectangle
。
另请参阅:
- What makes something a "trait object"?
- Is there any way to return a reference to a variable created in a function?