首页 > 解决方案 > 有没有一种内存有效的方法来改变固有实现的行为?

问题描述

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

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

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

如果有更好的设计,我愿意接受。foo最终,我想bar根据在MyData. 此外,我希望有一种方法可以在可能的情况下共享内存,并且我们能够更改单个功能而不仅仅是整个配置。

标签: rust

解决方案


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


推荐阅读