I have a simple Enum used to map to hardcoded strings:
Think:
enum SomeThing {
A, B, C
}
These values are shown in a UI as RadioButtons so the user can pick one and these options are mapped to a string (the name of a file I don't control).
The way this is done at runtime time is (pseudo-code):
let xxx: HashMap::from([
(
SomeThing::A.to_string(),
"value_for_a".to_string(),
),
(
SomeThing::B.to_string(),
"value_for_b".to_string(),
),
etc...
This is done at runtime and later used to retrieve the value for the chosen option. The fact that it's an Enum has the type safety in mind. But...
I'd like to change this code to make it user-configurable, and allow the user to specify those values themselves; since it's a runtime-constructed HashMap, I could imagine a config file (of any format like YAML) having:
---
xxx:
A: value_for_a
B: value_for_b
C: value_for_c
And parsed into "the same" HashMap (without the benefits of the Enum type safety).
The questions are:
I suppose creating an Enum at runtime would not be possible right? I'd imagine the user could create a new D key/value and or remove an existing one.
What would be another way to do this instead? Keep/Use a HashMap of some sort to store the Key (A,B,C) and their values after they were parsed?
(worth mentioning: the Enum is convenient because it's used by structopt for a command line parameter via the provided arg_enum! macro, so you can specify the posible_values as &YourEnum::variants() but this is less important to keep)
Is there a "better" way to do this that you can think of?
Related
I have been working on configuration parsing code and wondered if you could help me to pick the best types for this code.
I am trying to parse the following TOML:
[default]
a=10
b="abc"
[section1]
a = 78
[section2]
b="xyz"
The types of keys are the same in each section and each field follows the chain of defaults: sectionX.value => default.value => default value hardcoded in Rust via x.value.or(default.value).or(Some(...) for each field.
The most straightforward way to declare it in Rust (including serde attributes)
struct Section{
a: Option<usize>,
b: Option<String>,
}
The problem is that I want to parse all defaults first, and then use a fully materialized struct with no unassigned values in my code.
struct Section{
a: usize,
b: String,
}
I can use the following approaches:
Use original Section struct and always to unwrap/expect because I "know" the defaults have been assigned in the config parsing code. I make a mistake, I can catch panic and that does not look tidy. I'd like to leverage more help from the compiler.
Use the second Section struct (the one that has no Options). I can assign defaults via Serde annotations, but then I am loosing the signal that something was unspecified and needs a default from another level.
Declare both variants of the struct. This is the most verbose variant and I will look even more verbose when I grow 20+ fields or embedded structs in the config.
Generated Solution 3 via macros or some clever typing. Is there a crate for this? Maybe Section could have some generic type that can be Option in one place, but then "newtype" wrapper with a single value somewhere else?
Something else?
Some of the solutions above would work alright, but maybe there is a known elegant solution.
Sample code :
use serde_json::{json, Value};
fn main() {
let a: Rc<RefCell<Value>> = Rc::new(RefCell::new(json!({"a": "value"})));
let v: Rc<RefCell<Vec<Value>>> = Rc::new(RefCell::new(vec![]));
// How to push a into v, but keep `a` and `v` for future uses
}
I want to push Value a into vector v (link pointer), but still be able to use a later on.
I've tried the following:
1.
v.borrow_mut().push(*a.borrow_mut());
This does not work, as Value does not implement Copy trait, thus could not be dereferenced
2.
v.borrow_mut().push(RefCell::take(&a));
This works, however a has now lost the contents. I do understand that there should only be one owner, but I'm not sure how to achieve what I need.
The Why
I have a Value which I need to modify depending on some other data which is in a tree structure. I want to recursively walk through that other data tree, and pass along this Value to be modified.
In my function, I have this Value signature as Rc<RefCell<Value>>, and depending on the case I sometimes need to add nested properties, so I may need to add a property containing a vector with a new json!({}), and pass it as a new Rc<RefCell<Value>> to sub-iterations to append data to this specific subtree.
So, if you have any advice - maybe I need to change the recursive function's signature to have this data as Rc<RefCell<&Value>> instead, to have only one owner ? That seems like an awful lot of all sorts of references stacked upon one another, not sure which is the good way
Edit1: Updated sample to include Rc<RefCell<_>>
I'd like to additively deserialize multiple files over the same data structure, where "additively" means that each new file deserializes by overwriting the fields that it effectively contains, leaving unmodified the ones that it does not. The context is config files; deserialize an "app" config provided by the app, then override it with a per-"user" config file.
I use "file" hear for the sake of clarity; this could be any deserializing data source.
Note: After writing the below, I realized maybe the question boils down to: is there a clever use of #[serde(default = ...)] to provide a default from an existing data structure? I'm not sure if that's (currently) possible.
Example
Data structure
struct S {
x: f32,
y: String,
}
"App" file (using JSON for example):
{ "x": 5.0, "y": "app" }
"User" file overriding only "y":
{ "y": "user" }
Expected deserializing (app, then user):
assert_eq!(s.x, 5.0);
assert_eq!(s.y, "user");
Expected solution
I'm ignoring on purpose any "dynamic" solution storing all config settings into, say, a single HashMap; although this works and is flexible, this is fairly inconvenient to use at runtime, and potentially slower. So I'm calling this approach out of scope for this question.
Data structure can contain other structs. Avoid having to write too many per-struct code manually (like implementing Deserialize by hand). A typical config file for a moderate-sized app can contains hundreds of settings, I don't want the burden of having to maintain those.
All fields can be expected to implement Default. The idea is that the first deserialized file would fallback on Default::default() for all missing fields, while subsequent ones would fallback on already-existing values if not explicitly overridden in the new file.
Avoid having to change every single field of every single struct to Option<T> just for the sake of serializing/deserializing. This would make runtime usage very painful, where due to above property there would anyway be no None value ever once deserialization completed (since, if a field is missing from all files, it defaults to Default::default() anyway).
I'm fine with a solution containing only a fixed number (2) of overriding files ("app" and "user" in example above).
Current partial solution
I know how to do the first part of falling back to Default; this is well documented. Simply use #[serde(default)] on all structs.
One approach would be to simply deserialize twice with #[serde(default)] and override any field which is equal to its default in the app config with its value in the user config. But this 1) probably requires all fields to implement Eq or PartialEq, and 2) is potentially expensive and not very elegant (lose the info during deserialization, then try to somehow recreate it).
I have a feeling I possibly need a custom Deserializer to hold a reference/value of the existing data structure, which I would fallback to when a field is not found, since the default one doesn't provide any user context when deserializing. But I'm not sure how to keep track of which field is currently being deserialized.
Any hint or idea much appreciated, thanks!
Frustratingly, serde::Deserialize has a method called deserialize_in_place that is explicitly omitted from docs.rs and is considered "part of the public API but hidden from rustdoc to hide it from newbies". This method does exactly what you're asking for (deserialize into an existing &mut T object), especially if you implement it yourself to ensure that only provided keys are overridden and other keys are ignored.
C# 9 has a new feature. A record type. The record is just a class, but with a bunch of automatically created functions and properties. But basically the idea (as I undstand it) was, a class that behaves like structs, for things like copying, coimparison with Equals, immutibility and so on.
Also with the record type was a new feature with the keyword "with". To create a copy of a record, you can write something like that: var copy = original with { Property = new_value, };
Now I wondered, if records were designt to behave like structs (but are classes). Why doesn't the new "with" keyword works also with structs. I mean, as far as I can tell, structs have all features, that are necessary for this feature. Like they are copied by value.
Instead to use similar features for structs, I have to write a copy constructor and can then write: var copy = new StructType(original) { Property = new_value, };
Short answer:
That's how the feature was designed.
Long answer:
The compiler creates a synthesized clone method with a reserved name <Clone>$, when you use with keyword, the compiler calls this clone method, and then modifies whatever properties you want to modify.
structs or classes doesn't have a synthesized clone method. Hence, with can't be used with them.
You may want to write a language proposal to extend the usage of with keyword.
Edit:
Currently, there is a proposal for allowing record structs. See Proposal: record structs for more information. This is what you may want.
I am making a simple debugger window in ActionScript for myself where I can add and remove variables I want to track. I was to be able to add variables to the list by just doing something like
DebuggerMonitor.trackVar(variable).
My question is, is there any way I can turn "variable" itself (the name, not the value) into a String to be added into a text field?
Depending on how "intelligent" your debugger should be, you could just pass the name along:
DebuggerMonitor.trackVar( variable, "variable" );
since obviously, when used in a context like this, the name should be known at the time you are writing the program.
You can also do some reflection magic to get instance variable names, but it won't work for temp variables (their names are dropped at compilation time):
public function getVariableName( instance:*, match:* ):String {
var typeDescription:XML = describeType( instance );
var variables:XMLList = typeDescription..variable;
var accessors:XMLList = typeDescription..accessor;
for each(var variable:XML in variables)
if(matchesXMLName( instance, variable, match ))
return variable.#name;
for each(var accessor:XML in accessors)
if(matchesXMLName( instance, accessor, match ))
return accessor.#name;
return "No name found.";
}
private function matchesXMLName( instance:*, xml:XML, match:* ):Boolean {
return match == instance[xml.#name.toString()];
}
var varName:String = getVariableName ( myObject, variable );
Using reflections like this will also be quite costly, if used often - you will have to think of a way to cache the type descriptions.
I recommend you check out the as3commons reflections package - there is a lot of useful functionality in there...
Short answer - No :(
You can access the type name but not individual instance names, as these are lost at run-time.
There is a confusion caused by the keyword 'var' because it is used to create several types of bindings.
Lexical bindings (the keyword 'var' was used inside a function).
Dynamic bindings (the keyword 'var' was used to declare a class' field).
Lexical bindings are interpreted by the compiler at compile time as addresses of the registers of the registers space occupied by the function. The names given to lexical bindings perish at this time and it is not possible to restore them at runtime - therefore you can't get the "name" of the variable.
Dynamic bindings are a kind of "public API" of the objects that declare them, they may be accessed from the code that was not compiled together with the code that created them, this is why, for the purpose of reflection the names of these bindings are stored in compiled code. However, ActionScript has no way of referencing LHS values, so you cannot, even if you know the name of the variable and the object declaring it, pass it to another function. But you can look it up in the debugger or by calling describeType on the object declaring the variable. Note that describeType will not show information on private variables even if you are calling it from the scope of the object in question.