This is an alternative implementation to #440.
This implementation is notably different in that it is more controllable by the implementor and feels more like a first class feature.
The main changes are the addition of types deriving Any
to state the priority of dynamic fields vs actual fields. The options are as follows:
- First - Will try to find a dynamic field first before checking static fields.
- Last - Will try to find a static field first before checking dynamic fields.
- Never - Will completely disregard any dynamic fields and exclusively use static fields. (Same behavior as prior to this implementation and the default)
- Only - Will completely disregard static fields and exclusively use dynamic fields.
All of these can be applied to any type using the Derive(Any)
macro by applying the attribute
#[rune(dynamic_fields="never|only|first|last")]
to the struct itself.
Dynamic fields functionality is basically the same as the #440 however due to the fact First
and Only
exist means there needed to be a change to their implementation.
DYNAMIC_FIELD_GET
now handles Option
s slightly differently: if you return None
it will presume there was no field, Some(None)
is still a option if desired. The value will be unwrapped from the Option
on the callers side so doesn't need to be worried about by the script writer.
DYNAMIC_FIELD_SET
is basically the same as DYNAMIC_FIELD_GET
however instead of a Option
you can choose to return false
which will be treat as a failure to set the field, allowing it to try and set the static field next if the type is First
or simply return a error otherwise as expected with static fields.
For both of the above changes to DYNAMIC_FIELD_SET
and DYNAMIC_FIELD_GET
: you can return any other type (including ()
of course) and it will not treat it any differently to before.
Examples
A struct with dynamic fields which are prioritised over static fields:
#[derive(Any, Clone)]
#[rune(dynamic_fields="first")]
struct TestStruct {
values: HashMap<String, u32>,
b: u32,
c: u32
}
implementing fallible DYNAMIC_FIELD_GET and DYNAMIC_FIELD_SET
let mut module = Module::new();
module.ty::<TestStruct>()?;
module.inst_fn(
Protocol::DYNAMIC_FIELD_GET,
|this: &TestStruct, key: String| {
if let Some(v) = this.values.get(&key) {
Some(*v)
} else {
None
}
},
)?;
module.inst_fn(
Protocol::DYNAMIC_FIELD_SET,
|this: &mut TestStruct, key: String, value: u32| {
if key == "nerd" {
false
} else {
this.values.insert(key, value);
true
}
},
)?;
Reasoning
Sometimes it is desired for user readability to pretend a specific custom type has fields and instead accessing the data from elsewhere such as from a HashMap
or another collection.
possible use cases is having types with dynamic layouts while having rules to their usage such as locking the layout once created.
it also allows custom types to be dynamically extended by the scripter while keeping the benefits of being a static Shared<AnyObj>
but not seeming like a collection which []
accesses give the impression of.
Benchmarks
Note
Due to the tiny footprints of the functions, the differences are very hard to see if any and can be improved within my benchmarks with a faster hashmap regardless but honestly values these small are easily within margin of error.
Before
test fields::actual_field_get ... bench: 418 ns/iter (+/- 88)
test fields::actual_field_set ... bench: 434 ns/iter (+/- 161)
test fields::string_key_classic_index_get ... bench: 523 ns/iter (+/- 52)
test fields::static_string_key_classic_index_get ... bench: 486 ns/iter (+/- 80)
After
test meta_fields::actual_field_get_first ... bench: 614 ns/iter (+/- 18)
test meta_fields::actual_field_get_last ... bench: 420 ns/iter (+/- 46)
test meta_fields::actual_field_get_never ... bench: 414 ns/iter (+/- 27)
test meta_fields::actual_field_set_first ... bench: 514 ns/iter (+/- 63)
test meta_fields::actual_field_set_last ... bench: 427 ns/iter (+/- 14)
test meta_fields::actual_field_set_never ... bench: 408 ns/iter (+/- 28)
test meta_fields::actual_field_set_only ... bench: 515 ns/iter (+/- 16)
test meta_fields::static_string_index_get_first ... bench: 467 ns/iter (+/- 14)
test meta_fields::static_string_index_get_last ... bench: 471 ns/iter (+/- 35)
test meta_fields::static_string_index_get_never ... bench: 465 ns/iter (+/- 43)
test meta_fields::static_string_index_get_only ... bench: 459 ns/iter (+/- 17)
test meta_fields::static_string_meta_field_get_first ... bench: 531 ns/iter (+/- 64)
test meta_fields::static_string_meta_field_get_last ... bench: 546 ns/iter (+/- 31)
test meta_fields::static_string_meta_field_get_only ... bench: 518 ns/iter (+/- 35)
test meta_fields::static_string_meta_field_set_first ... bench: 525 ns/iter (+/- 231)
test meta_fields::static_string_meta_field_set_last ... bench: 545 ns/iter (+/- 42)
test meta_fields::static_string_meta_field_set_only ... bench: 515 ns/iter (+/- 34)
test meta_fields::string_index_get_first ... bench: 504 ns/iter (+/- 24)
test meta_fields::string_index_get_last ... bench: 505 ns/iter (+/- 42)
test meta_fields::string_index_get_never ... bench: 504 ns/iter (+/- 17)
test meta_fields::string_index_get_only ... bench: 501 ns/iter (+/- 20)
test meta_fields::string_meta_field_get_first ... bench: 552 ns/iter (+/- 21)
test meta_fields::string_meta_field_get_last ... bench: 595 ns/iter (+/- 26)
test meta_fields::string_meta_field_get_only ... bench: 558 ns/iter (+/- 26)
test meta_fields::string_meta_field_set_first ... bench: 508 ns/iter (+/- 23)
test meta_fields::string_meta_field_set_last ... bench: 553 ns/iter (+/- 39)
test meta_fields::string_meta_field_set_only ... bench: 510 ns/iter (+/- 23)
Closes
#381 #263
enhancement lang