rust - 有没有一种内存有效的方法来改变固有实现的行为?
问题描述
是否有一种内存有效的方法来改变固有实现的行为?目前,我可以通过存储一些函数指针来完成行为的改变,然后由固有实现调用它们。我的困难是可能有大量这样的函数和大量依赖于这些函数的对象,所以我想减少使用的内存量。例如,考虑以下代码:
// Holds the data for some process
struct MyData {
x: f64,
y: f64,
fns: MyFns,
}
impl MyData {
// Create a new object
fn new(x: f64, y: f64) -> MyData {
MyData {
x,
y,
fns: CONFIG1,
}
}
// One of our functions
fn foo(&self) -> f64 {
(self.fns.f)(self.x, self.y)
}
// Other function
fn bar(&self) -> f64 {
(self.fns.g)(self.x, self.y)
}
}
// Holds the functions
struct MyFns {
f: fn(x: f64, y: f64) -> f64,
g: fn(x: f64, y: f64) -> f64,
}
// Some functions to use
fn add(x: f64, y: f64) -> f64 {
x + y
}
fn sub(x: f64, y: f64) -> f64 {
x - y
}
fn mul(x: f64, y: f64) -> f64 {
x * y
}
fn div(x: f64, y: f64) -> f64 {
x / y
}
// Create some configurations
const CONFIG1: MyFns = MyFns {
f: add,
g: mul,
};
const CONFIG2: MyFns = MyFns {
f: sub,
g: div,
};
fn main() {
// Create our structure
let mut data = MyData::new(1., 2.);
// Check our functions
println!(
"1: x={}, y={}, foo={}, bar={}",
data.x,
data.y,
data.foo(),
data.bar()
);
// Change the functions
data.fns = CONFIG2;
// Print the functions again
println!(
"2: x={}, y={}, foo={}, bar={}",
data.x,
data.y,
data.foo(),
data.bar()
);
// Change a single function
data.fns.f = add;
// Print the functions again
println!(
"3: x={}, y={}, foo={}, bar={}",
data.x,
data.y,
data.foo(),
data.bar()
);
}
此代码允许通过编辑foo
和bar
来更改f
和的行为g
。但是,它也不灵活。我宁愿使用盒装特征对象Box<dyn Fn(f64,f64)->f64
,但是我不能创建一些默认配置,比如CONFIG1
andCONFIG2
因为Box
不能用于创建常量对象。另外,如果我们有大量的函数和对象,我想为它们的实现共享内存。对于函数指针,这没什么大不了的,但对于闭包来说却是。在这里,我们不能创建一个常量Rc
用于配置共享内存。最后,我们可以对配置进行静态引用,这将节省内存,但我们无法更改各个函数。我宁愿我们有一种情况,大多数时候我们为函数共享内存,但有能力拥有自己的内存并在需要时更改函数。
如果有更好的设计,我愿意接受。foo
最终,我想bar
根据在MyData
. 此外,我希望有一种方法可以在可能的情况下共享内存,并且我们能够更改单个功能而不仅仅是整个配置。
解决方案
一个普通的dyn
引用将在这里工作 - 它允许引用具有特定特征但类型仅在运行时已知的对象。
(这正是你想要的函数指针。把它想象成每个函数都有自己的特殊类型,但属于类似的特征Fn(f64,f64)->f64
。)
所以你的结构可以定义为:
struct MyData<'a> {
x: f64,
y: f64,
f: &'a dyn Fn(f64, f64) -> f64,
g: &'a dyn Fn(f64, f64) -> f64,
}
(注意,你需要生命周期说明符'a
来确保引用的生命周期不短于结构本身。)
那么你的 impl 可能是这样的:
impl<'a> MyData<'a> {
// Create a new object
fn new(x: f64, y: f64) -> Self {
MyData {
x,
y,
f: &add, // f and g as in CONFIG1
g: &mul,
}
}
fn foo(&self) -> f64 {
(self.f)(self.x, self.y)
}
// etc...
}
根据您希望默认配置如何工作,您可以将它们作为更固有的函数,例如,fn to_config2(&mut self);
或者您可以仅使用函数指针创建一个单独的结构,然后使用一个函数将这些函数指针复制到MyData
结构中。
推荐阅读
- c# - 如何动态更改我在 MVVM XAML 中使用的控件
- ios - 适合多级/屏幕游戏的场景套件架构
- file - UWP 打开文件问题
- excel - 函数属性数据透视表类
- date - 如何在一个应用程序中连贯地使用 RRULE 日期时间序列和任意的非 RRULE 日期时间序列
- android - Android 10:IMEI 在 API 29 上不再可用。寻找替代品
- java - 当我加载 openSourceBim IfcModelService 时,它总是为空
- ios - Xcode 11 无法将 Info.plist 重新分配到一个组中
- prisma - 使用 nexus 在 Prisma2 中公开总页数的最佳方法是什么?
- amazon-web-services - 如何找到离特定网站最近的 EC2 可用区