struct Point {
x: f32,
y: f32,
}
fn main() {
let point = Point { x: 0.3, y: 0.4 };
println!("point coordinates: ({}, {})",
point.x, point.y);
let sum_of_odd: i64 = (1..10)
.filter(|n| n % 2 != 0)
.sum();
println!("{}", sum_of_odd);
}
fn main() {
let elem = true;
let mut vec = Vec::new();
vec.push(elem);
// vec must be Vec<bool>
vec.push(5);
// type error: expected bool, found integral variable
}
enum Option<T> {
None,
Some(T),
}
fn main() {
match might_fail() {
Some(n) => println!("It was {}", n),
None => println!("No result"),
}
}
{
"id": 745823,
"name": "Bob",
"dob": "1985-02-22",
"phoneNumber": "81549300",
"aliases": ["Robert", "Bobbie"],
"address": {
"street": "Pleasant Road 5",
"postalCode": "3490"
}
}
pub enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
Array(Vec<Value>),
Object(Map<String, Value>),
}
{
"id": 745823,
"name": "Bob",
"dob": "1985-02-22",
"phoneNumber": "81549300"
}
fn main() {
let v = somehow::parse::<Value>();
if let Value::Object(fields) = v {
if let Some(number) = fields.get("phoneNumber") {
if let Value::String(s) = number {
println!("{}", s);
}
}
}
}
pub enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
Array(Vec<Value>),
Object(Map<String, Value>),
}
{
"id": 745823,
"name": "Bob",
"dob": "1985-02-22",
"phoneNumber": "81549300"
}
fn main() {
let v = somehow::parse::<Value>();
println!("{}", v.pointer("/phoneNumber").unwrap()
.as_str().unwrap());
}
pub struct Customer {
id: i64,
name: String,
dob: Date,
phone_number: String
}
{
"id": 745823,
"name": "Bob",
"dob": "1985-02-22",
"phoneNumber": "81549300"
}
fn main() {
let v = somehow::parse::<Customer>();
println!("{}", v.phone_number);
}
{"id":"AN621C0S5-K11","modelId":"AN621C0S5","name":"Summer dress - blue/white","shopUrl":"https://www.zalando.co.uk/anna-field-summer-dress-blue-white-an621c0s5-k11.html",
"color":"Blue","available":true,"season":"WINTER","seasonYear":"2016","activationDate":"2015-04-30T16:52:52+02:00","additionalInfos":[],"tags":[],"genders":["FEMALE"],
"ageGroups":["ADULT"],"brand":{"key":"AN6","name":"Anna Field","logoUrl":"https://i6.ztat.net/brand/anna-field.jpg","logoLargeUrl":"https://i6.ztat.net/brandxl/anna-field.jpg",
"brandFamily":{"key":"AN00","name":"Anna Field","shopUrl":"https://www.zalando.co.uk/anna-field-online-shop"},"shopUrl":"https://www.zalando.co.uk/anna-field"},
"categoryKeys":["catalog","women","womens-sale","womens-clothing-sale","womens-dresses-sale","all","sale","summer-dresses-sale"],"attributes":[{"name":"Outer fabric material","values":["100% cotton"]},{"name":"Total length","values":["34.0 \" (Size 8)"]},{"name":"Insert material","values":["97% viscose, 3% spandex"]},{"name":"Fabric","values":["Jersey"]},{"name":"Details","values":["belt included"]},{"name":"Length","values":["short"]},{"name":"Top part material","values":["97% viscose, 3% spandex"]},{"name":"Fit","values":["tailored"]},{"name":"Pattern","values":["striped"]},{"name":"Neckline","values":["round neck"]},
{"name":"Washing instructions","values":["do not tumble dry","machine wash at 30°C","Machine wash on gentle cycle"]},{"name":"Sleeve length","values":["Extra short"]},{"name":"Our model's height","values":["Our model is 70.0 \" tall and is wearing size 8"]}],"units":[{"id":"AN621C0S5-K110340000","size":"6","price":{"currency":"GBP","value":14.69,"formatted":"£14.69"},"originalPrice":{"currency":"GBP","value":20.99,"formatted":"£20.99"},"available":false,"stock":0},{"id":"AN621C0S5-K110360000","size":"8","price":{"currency":"GBP","value":14.69,"formatted":"£14.69"},"originalPrice":{"currency":"GBP","value":20.99,"formatted":"£20.99"},"available":false,"stock":0},
{"id":"AN621C0S5-K110440000","size":"16","price":{"currency":"GBP","value":14.69,"formatted":"£14.69"},"originalPrice":{"currency":"GBP","value":20.99,"formatted":"£20.99"},"available":true,"stock":3},{"id":"AN621C0S5-K110420000","size":"14","price":{"currency":"GBP","value":14.69,"formatted":"£14.69"},"originalPrice":{"currency":"GBP","value":20.99,"formatted":"£20.99"},"available":false,"stock":0},{"id":"AN621C0S5-K110380000","size":"10","price":{"currency":"GBP","value":14.69,"formatted":"£14.69"},"originalPrice":{"currency":"GBP","value":20.99,"formatted":"£20.99"},"available":false,"stock":0},{"id":"AN621C0S5-K110400000","size":"12","price":{"currency":"GBP","value":14.69,"formatted":"£14.69"},"originalPrice":{"currency":"GBP","value":20.99,"formatted":"£20.99"},"available":false,"stock":0}],"media":{"images":[
{"orderNumber":1,"type":"NON_MODEL","thumbnailHdUrl":"https://i2.ztat.net/thumb_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@16.jpg","smallUrl":"https://i2.ztat.net/catalog/AN/62/1C/0S/5K/11/AN621C0S5-K11@16.jpg","smallHdUrl":"https://i2.ztat.net/catalog_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@16.jpg",
"mediumUrl":"https://i2.ztat.net/detail/AN/62/1C/0S/5K/11/AN621C0S5-K11@16.jpg","mediumHdUrl":"https://i2.ztat.net/detail_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@16.jpg","largeUrl":"https://i2.ztat.net/large/AN/62/1C/0S/5K/11/AN621C0S5-K11@16.jpg","largeHdUrl":"https://i2.ztat.net/large_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@16.jpg"},
{"orderNumber":2,"type":"STYLE","thumbnailHdUrl":"https://i3.ztat.net/thumb_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@15.jpg","smallUrl":"https://i3.ztat.net/catalog/AN/62/1C/0S/5K/11/AN621C0S5-K11@15.jpg","smallHdUrl":"https://i3.ztat.net/catalog_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@15.jpg","mediumUrl":"https://i3.ztat.net/detail/AN/62/1C/0S/5K/11/AN621C0S5-K11@15.jpg","mediumHdUrl":"https://i3.ztat.net/detail_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@15.jpg",
"largeUrl":"https://i3.ztat.net/large/AN/62/1C/0S/5K/11/AN621C0S5-K11@15.jpg","largeHdUrl":"https://i3.ztat.net/large_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@15.jpg"},
{"orderNumber":3,"type":"PREMIUM","thumbnailHdUrl":"https://i6.ztat.net/thumb_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@14.jpg","smallUrl":"https://i6.ztat.net/catalog/AN/62/1C/0S/5K/11/AN621C0S5-K11@14.jpg","smallHdUrl":"https://i6.ztat.net/catalog_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@14.jpg","mediumUrl":"https://i6.ztat.net/detail/AN/62/1C/0S/5K/11/AN621C0S5-K11@14.jpg","mediumHdUrl":"https://i6.ztat.net/detail_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@14.jpg","largeUrl":"https://i6.ztat.net/large/AN/62/1C/0S/5K/11/AN621C0S5-K11@14.jpg","largeHdUrl":"https://i6.ztat.net/large_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@14.jpg"},
{"orderNumber":4,"type":"PREMIUM","thumbnailHdUrl":"https://i5.ztat.net/thumb_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@13.jpg","smallUrl":"https://i5.ztat.net/catalog/AN/62/1C/0S/5K/11/AN621C0S5-K11@13.jpg","smallHdUrl":"https://i5.ztat.net/catalog_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@13.jpg","mediumUrl":
"https://i5.ztat.net/detail/AN/62/1C/0S/5K/11/AN621C0S5-K11@13.jpg","mediumHdUrl":"https://i5.ztat.net/detail_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@13.jpg","largeUrl":"https://i5.ztat.net/large/AN/62/1C/0S/5K/11/AN621C0S5-K11@13.jpg","largeHdUrl":"https://i5.ztat.net/large_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@13.jpg"},
{"orderNumber":5,"type":"PREMIUM","thumbnailHdUrl":"https://i4.ztat.net/thumb_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@12.jpg","smallUrl":"https://i4.ztat.net/catalog/AN/62/1C/0S/5K/11/AN621C0S5-K11@12.jpg",
"smallHdUrl":"https://i4.ztat.net/catalog_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@12.jpg","mediumUrl":"https://i4.ztat.net/detail/AN/62/1C/0S/5K/11/AN621C0S5-K11@12.jpg","mediumHdUrl":"https://i4.ztat.net/detail_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@12.jpg","largeUrl":"https://i4.ztat.net/large/AN/62/1C/0S/5K/11/AN621C0S5-K11@12.jpg","largeHdUrl":"https://i4.ztat.net/large_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@12.jpg"},
{"orderNumber":6,"type":"PREMIUM","thumbnailHdUrl":"https://i1.ztat.net/thumb_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@11.jpg","smallUrl":"https://i1.ztat.net/catalog/AN/62/1C/0S/5K/11/AN621C0S5-K11@11.jpg","smallHdUrl":"https://i1.ztat.net/catalog_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@11.jpg","mediumUrl":"https://i1.ztat.net/detail/AN/62/1C/0S/5K/11/AN621C0S5-K11@11.jpg",
"mediumHdUrl":"https://i1.ztat.net/detail_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@11.jpg","largeUrl":"https://i1.ztat.net/large/AN/62/1C/0S/5K/11/AN621C0S5-K11@11.jpg","largeHdUrl":"https://i1.ztat.net/large_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@11.jpg"},
{"orderNumber":7,"type":"PREMIUM","thumbnailHdUrl":"https://i6.ztat.net/thumb_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@10.jpg","smallUrl":"https://i6.ztat.net/catalog/AN/62/1C/0S/5K/11/AN621C0S5-K11@10.jpg","smallHdUrl":"https://i6.ztat.net/catalog_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@10.jpg","mediumUrl":"https://i6.ztat.net/detail/AN/62/1C/0S/5K/11/AN621C0S5-K11@10.jpg","mediumHdUrl":
"https://i6.ztat.net/detail_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@10.jpg","largeUrl":"https://i6.ztat.net/large/AN/62/1C/0S/5K/11/AN621C0S5-K11@10.jpg","largeHdUrl":"https://i6.ztat.net/large_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@10.jpg"},
{"orderNumber":8,"type":"PREMIUM","thumbnailHdUrl":"https://i5.ztat.net/thumb_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@9.jpg","smallUrl":"https://i5.ztat.net/catalog/AN/62/1C/0S/5K/11/AN621C0S5-K11@9.jpg","smallHdUrl":"https://i5.ztat.net/catalog_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@9.jpg","mediumUrl":"https://i5.ztat.net/detail/AN/62/1C/0S/5K/11/AN621C0S5-K11@9.jpg","mediumHdUrl":"https://i5.ztat.net/detail_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@9.jpg",
"largeUrl":"https://i5.ztat.net/large/AN/62/1C/0S/5K/11/AN621C0S5-K11@9.jpg","largeHdUrl":"https://i5.ztat.net/large_hd/AN/62/1C/0S/5K/11/AN621C0S5-K11@9.jpg"}]}}
A feature introduced in version 3.0 (in August 2012) of the programming language F# for strongly typed interaction with external elements.
E.g. a web API or a database.
The library F# Data in action:
type Simple = JsonProvider<""" { "name":"John", "age":94 } """>
let simple = Simple.Parse(""" { "name":"Tomas", "age":4 } """)
simple.Age
simple.Name
compile time
runtime
type Simple = JsonProvider<"http://example.com/api/person/bob">
let simple = Simple.Parse(""" { "name":"Tomas", "age":4 } """)
simple.Age
simple.Name
Γ ⊢ e : τ
Normally when running type inference and checking we start with an empty initial context which is then extended by the code.
W'([ ], Π)
W'(Γ, let x = e1 in e2) =
let τ = W'(Γ, e1) in W'(Γ[x ↦ τ], e2)
Type providers introduces a way to project some information from the outside world into the typing context.
W'(π(🌍), Π)
W'([ ], Π)
Sounds great, but Rust does not have type providers...
While Rust does not have builtin support for type providers, it does have macros.
fn procedural_macro(input: Tokens) -> Tokens {
...
}
macro_rules! some_if {
($cond:expr, $then:expr) => ({
if $cond {
Some($then)
} else {
None
}
})
}
pub struct Customer {
id: i64,
name: String,
dob: Date,
phone_number: String
}
#[derive(Serialize, Deserialize)]
pub struct Customer {
id: i64,
name: String,
dob: Date,
phone_number: String
}
generates code for serialization and deserialization
impl Serialize for Customer {
fn serialize() -> Result<Customer, Error> {
...
}
}
infer_schema!("dotenv:DATABASE_URL");
generates code by talking to your database
// types and functions as necessary
// depending on the actual schema of the database
Code that talks to the database (at compile time)
json_provider!("Point", r#"{ "x": 3, "y": 5 }"#);
A procedural macro generating Rust types from inline, local or remote JSON samples.
Type providers does not give you (as the user) access to the generated code*.
*: F# Data creates erased types, so it does not really have normal code to give you even if it wanted to.
So what happens if you want to stop using the type provider?
{
"id": 745823,
"name": "Bob",
"dob": "1985-02-22",
"phoneNumber": "81549300"
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
struct Point {
id: i64,
name: String,
dob: String,
#[serde(rename = "phoneNumber")]
phone_number: String,
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
struct Point {
id: i64,
name: String,
dob: Date,
#[serde(rename = "phoneNumber")]
phone_number: String,
}
Generated:
We want:
Sample:
Since procedural macros are basically functions from Tokens to Tokens, we don't have to run them at compile time.
As such my project has three different interfaces (to the same code generation):
Presented in a paper by the authors of the library F# Data: http://tomasp.net/academic/papers/fsharp-data/
fn infer_shape_from_value(value: Value) -> InferredType {
match value {
Value::Bool(_) => InferredType::Bool,
Value::Number(ref n) => {
if n.is_i64() {
InferredType::Integer
} else {
InferredType::Floating
}
},
...
}
}
fn common_shape(a: InferredType, b: InferredType)
-> InferredType {
if a == b {
return a;
}
use InferredType::*;
match (a, b) {
(Floating, Integer) => Floating,
...
_ => Any,
}
}
// For Value::Array(values)
let inner = values.fold(shape_of_first, |shape, value| {
let new_shape = infer_shape_from_value(value);
common_shape(shape, new_shape)
});
// InferredType::VecT(inner)
fn generate_type_from_inferred(inferred: InferredType) -> Tokens {
match inferred {
InferredType::Null |
InferredType::Any => quote! { ::serde_json::Value },
InferredType::Bool => quote! { bool },
...
}
}
#[cfg(not(feature = "online-samples"))]
json_provider!("Point", r#"{ "x": 1, "y": 2 }"#);
#[cfg(feature = "online-samples")]
json_provider!("Point", "http://vestera.as/json_sample/examples/point.json");
#[cfg(not(feature = "online-samples"))]
mod point;
#[cfg(feature = "online-samples")]
mod point {
json_provider!("Point", "http://vestera.as/json_sample/examples/point.json");
}
cargo check --features "online-samples"
In your Rust code:
Command line/shell script:
There are still many missing features, unhandled edge cases, etc.
F# Data, which has a type provider from JSON samples, also has type providers for XML and CSV based on the same inference code.
While JSON itself is schemaless, the JSON Schema standard (json-schema.org) defines a way to write schemas for JSON documents.