#tracker #track #change

tracker-macros

Tracker crate的宏

11个版本

0.2.2 2024年7月20日
0.2.1 2023年3月20日
0.2.0 2022年11月30日
0.1.7 2022年9月27日
0.1.0 2021年7月27日

#1620数据结构

Download history 749/week @ 2024-05-03 759/week @ 2024-05-10 603/week @ 2024-05-17 621/week @ 2024-05-24 723/week @ 2024-05-31 558/week @ 2024-06-07 750/week @ 2024-06-14 814/week @ 2024-06-21 490/week @ 2024-06-28 460/week @ 2024-07-05 644/week @ 2024-07-12 1030/week @ 2024-07-19 989/week @ 2024-07-26 988/week @ 2024-08-02 892/week @ 2024-08-09 574/week @ 2024-08-16

3,616 每月下载量
18 个crate中使用(通过 tracker

Apache-2.0 OR MIT

15KB
238

Tracker - 高效跟踪结构体的变化

Rust Tracker on crates.io Tracker on docs.rs

Tracker是一个小型crate,允许您跟踪结构体字段的变化。

它为您结构体字段实现了以下方法

  • get_#field_name()
    获取对field_name的不可变引用

  • get_mut_#field_name()
    获取对field_name的可变引用。假设该字段将被修改,并将其标记为已更改。

  • set_#field_name(value)
    设置field_name的值。只有当新值与旧值不相等时,才会将字段标记为已更改。

  • update_#field_name(fn)
    使用函数或闭包更新您的field_name。假设该字段将被修改,并将其标记为已更改。

  • changed_#field_name()
    检查field_name的值是否已更改。

要显式检查更改,您可以调用var_name.changed(StructName::field_name()),它将返回一个布尔值。可以使用var_name.changed(StructName::field_name_1() | StructName::field_name_2())检查多个字段。最后,可以使用var_name.changed(StructName::track_all())或其快捷方式var_name.changed_any()检查所有更改。

要重置所有以前的更改,您可以调用var_name.reset()

工作原理

让我们看看一个小例子。

#[tracker::track]
struct Test {
    x: u8,
    y: u64,
}

fn main() {
    let mut t = Test {
        x: 0,
        y: 0,
        // the macro generates a new variable called
        // "tracker" that stores the changes
        tracker: 0,
    };

    t.set_x(42);
    // let's check whether the change was detected
    assert!(t.changed(Test::x()));

    // reset t so we don't track old changes
    t.reset();

    t.set_x(42);
    // same value so no change
    assert!(!t.changed(Test::x()));
}

当您调用set_x()时,幕后发生的事情是在您结构体的跟踪字段中设置一个位标志

                                        y   x
tracker: u8 = | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
set_x(42)  -> | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
reset()    -> | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |

如您所见,这非常高效。宏展开看起来是这样的

impl Test {
    pub fn get_x(&self) -> &u8 {
        &self.x
    }
    pub fn get_mut_x(&mut self) -> &mut u8 {
        self.tracker |= Self::x();
        &mut self.x
    }
    pub fn update_x<F: Fn(&mut u8)>(&mut self, f: F) {
        self.tracker |= Self::x();
        f(&mut self.x);
    }
    pub const fn x() -> u8 {
        1 << 0usize
    }
    pub fn set_x(&mut self, value: u8) {
        if self.x != value {
        self.tracker |= Self::x();
        }
        self.x = value;
    }
}

其他属性

#[tracker::track]
struct Test {
    #[tracker::do_not_track]
    a: u8,
    #[do_not_track]
    b: u8,
    #[tracker::no_eq]
    c: u8,
    #[no_eq]
    d: u8,
}

您可以将字段标记为

  • do_not_track 如果您不想跟踪器为该字段实现任何功能
  • no_eq 如果字段的类型没有实现 PartialEq 或跟踪器在调用 set_#field_name(value) 时不应检查相等性,这样即使用相同的值覆盖也标记字段为已更改。

依赖关系

~270–720KB
~17K SLoC