#properties #dynamic #typed #object #subject #extending #static

dynprops

通过类型化动态属性创建和扩展对象

1 个不稳定版本

0.1.0 2021 年 9 月 9 日

#1159 in 数据结构

MIT/Apache

29KB
455

一个 Rust crate,用于创建和扩展具有类型化动态属性的对象。

示例

use dynprops::{Subject, Dynamic};

let subject = Subject::new();
let prop_a = subject.new_prop_const_init(5);
let prop_b = subject.new_prop_const_init("Foo");
let mut obj = Dynamic::new(&subject);
assert_eq!(obj[&prop_a], 5);
assert_eq!(obj[&prop_b], "Foo");

// Properties can be changed on a mutable object
obj[&prop_b] = "Foobar";
assert_eq!(obj[&prop_b], "Foobar");

// New properties can be introduced after an object is already created
let prop_c = subject.new_prop_default_init::<u32>();
assert_eq!(obj[&prop_c], 0u32);

// Properties can be initialized based on a function of other properties on the object
let prop_d = subject.new_prop_fn_init(|obj| obj[&prop_b].len());
assert_eq!(obj[&prop_d], 6);

使用场景:几乎静态资源

您可能熟悉 lazy_staticstatic_init crate。这些允许您使用任意复杂初始化器定义不可变的 static 值。然后可以在程序中的任何位置、任何线程中使用这些值。这对于需要无上下文构造的可重用资源来说是一个很好的模式,例如,将名称映射到硬编码函数的查找表。这当然比预先构造每个资源并将其传递给可能使用它们的每个函数要简单得多。它对于模块化也非常好,因为您只需在需要资源的模块中定义资源,其他模块无需了解这些资源。

但是对于只需要一点上下文的资源怎么办?例如,在 GPU 上加载的特定网格或数据库中的存储过程。没有上下文(GPU 设备句柄、数据库连接)我们无法构造这些资源,而这些资源仍然是常见的且可能可重用的。您是否要作弊并强迫上下文进入一个 static 变量,从而招致可怕的“全局状态”的愤怒?或者您完全放弃静态变量,回到手动预先初始化每个可能需要的资源的谦卑生活?

使用此 crate,您不必选择。您可以将这些资源作为“类型化动态属性”附加到上下文上,它们将在首次访问时自动初始化。让我们看看 GPU 网格示例。我们在这里使用的上下文是一个 wgpu::Device。首先,我们会定义一个 Subject 来封装此上下文的属性

use static_init::dynamic;
use dynprops::{Subject, Extended};

#[dynamic]
pub static DEVICE: Subject<wgpu::Device> = Subject::new();

然后,我们会定义一个与主题相关联的 Property。在这种情况下,一个立方体网格

pub struct Mesh {
  vertex_buffer: wgpu::Buffer,
  index_buffer: wgpu::Buffer,
  index_count: usize
}

pub fn build_cube_mesh(device: &wgpu::Device) -> Mesh {
  // ...
}

#[dynamic]
pub static CUBE_MESH: DynInitProperty<'static, wgpu::Device, Mesh> = DEVICE
    .new_prop_fn_init(|device| build_cube_mesh(&device.value))
    .into_dyn_init();

当我们最初创建设备时,我们必须将其包装在 Extended 中,以便它可以从特定的 Subject 访问属性

let device = create_device();
let device = Extended::new_extend(device, &DEVICE);

然后,每当我们需要访问我们的网格时,例如用于渲染,我们只需访问扩展设备上的属性即可。

pub fn render_cube(device: &Extended<'static, wgpu::Device>) {
  let cube_mesh = device[&CUBE_MESH];
  cube_mesh.render(&device.value); // Use .value to access the base value for an Extended
}

就这样,我们定义了一个可重用的资源,我们可以使用它而无需显式传递或全局状态。

无运行时依赖。