Razvan Rotari shows an experiment to see how far you can go with binding in C++; Cristian Neamtu follows with an insight on how to achieve this in Rust using Serde.
10. Idea 1- Pass object description as parameter
- No direct link between object and description
- Boilerplate
- Does not exposes implementation details
11. Idea 2- Automatic description detection
template <>
struct Descriptor {
static auto getDescription() {
return std::make_tuple(
std::make_pair("a", &SimpleStruct::a),
std::make_pair("b", &SimpleStruct::b),
std::make_pair("c", &SimpleStruct::c));
}
};
const auto output = readJson<SimpleStruct>(jsonData);
13. Idea 2- Automatic description detection
- Even more boilerplate
- Longer compilation times
- Simpler interface
- Does not exposes implementation details
14. Idea 3 - Embed description into a type
//Requires c++20
template <>
struct Descriptor {
using type = std::tuple<
SETTER("a", &SimpleStruct::a),
SETTER("b", &SimpleStruct::b),
SETTER("c", &SimpleStruct::c)>;
};
const auto output = readJson<SimpleStruct>(jsonData);
20. Conclusions
- Good to hide implementation
- In practice the keys need to be provided so the boilerplate is reduced
- Works better for complex data, like the output of a SQL JOIN
- Serialization can use the same description
- Flexible pattern
22. What Why Rust?
● Focused on memory safety
● No classes, no inheritance
● Traits over Interfaces
● No variadics in general
● Types can be deduced implicitly
● macro_rules! and procedural macros
23. The Ownership system
1. Each value has an owner
2. There can be only one owner at a time
3. When the owner goes out of scope the value gets dropped
The compiler:
● checks if the rules are respected
● checks the lifetimes of the data
● inserts clean up code during compilation
Code Examples
24. Traits
● similar to the idea of interfaces with some differences
● define a contract that must be supported by the desired data type
● can define include both static and instance specific methods
● can provide default implementations
● can be attached to any datatype (owned code standard-library 3rd-party)
Code Examples
25. macro_rules!
macro_rules! map {
( $($key:expr => $value:expr),* ) => {{
let mut map = HashMap::new();
$(map.insert($key, $value); )*
map
}};
}
fn main() {
let person = map!(
"name" => "John",
"surname" => "Brown"
);
println!("{:?}", person);
}
● Pattern matched
● Token-type safe
○ ty - types
○ ident - identifiers
○ expr - expressions
○ etc.
● Steep learning curve
● Must produce syntactically correct code
● Rust Playground Link
26. Procedural macros
● a compiler plugin written in Rust that is loaded during compilation
○ it receives the (method, struct, etc.) AST as a parameter
○ produces another AST (rust syntax)
27. struct SimpleVisitor;
impl Visitor for SimpleVisitor {
type Value = Simple;
type Error = BindError;
fn visit_map(self, mut map: MapAccess) -> Result<Simple, BindError> {
Ok(Simple {
a: map.get_value("a")?.unwrap(),
b: map.get_value("b")?.unwrap(),
c: map.get_value("c")?.unwrap(),
})
}
}
How can I get from this?
32. Conclusions
● No boilerplate (due to the powerful macro system)
○ Serde: Extensive configurability (renaming, transforming or ignoring attributes, etc.)
○ Type safe conversions
● Serde efficiency due zero-copy deserialization
○ Benchmark - comparison
● Serde has been widely adopted as the “standard” for serialization and
deserialization in Rust:
○ https://serde.rs/#data-formats (json, yaml, csv, etc.)