是否有一种节省内存的方法来更改固有实现的行为?



是否有一种内存有效的方法来更改固有实现的行为? 目前,我可以通过存储许多函数指针来完成行为的改变,然后由固有实现调用这些函数指针。 我的困难在于可能存在大量此类函数和大量依赖于这些函数的对象,因此我想减少使用的内存量。 例如,请考虑以下代码:

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

此代码允许通过编辑fg来更改foobar的行为。 但是,它也不灵活。 我宁愿使用盒装特征对象Box<dyn Fn(f64,f64)->f64,但是我无法创建一些默认配置,例如CONFIG1CONFIG2Box因为它们不能用于创建常量对象。 此外,如果我们有大量的函数和对象,我想分享它们的实现内存。 对于函数指针,这没什么大不了的,但对于闭包来说却是。 在这里,我们无法为配置创建常量Rc以共享内存。 最后,我们可以对配置进行静态引用,这将节省内存,但随后我们无法更改各个函数。 我宁愿我们遇到这样一种情况,即大多数时候我们共享函数的内存,但能够保留自己的内存并在需要时更改函数。

如果有更好的设计,我愿意接受。 最终,我想根据MyData内部以某种形式持有的函数来更改运行时foobar的行为。 此外,我想要一种方法,在可能的情况下共享内存,并且我们能够更改单个功能而不仅仅是整个配置。

一个普通的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结构中。

最新更新