-
-
Save bohdaq/4881924b363ae69dda7eef1204d15a27 to your computer and use it in GitHub Desktop.
// real world example from json module of rust-web-server: https://github.com/bohdaq/rust-web-server/tree/main/src/json | |
// defining generics, library code | |
pub struct JSONArrayOfObjects<T> { | |
_item: T, // added to eliminate compiler error | |
} | |
pub trait New { | |
fn new() -> Self; | |
} | |
impl<T: New> JSONArrayOfObjects<T> { | |
pub fn new() -> T { | |
T::new() | |
} | |
} | |
impl<T: ToJSON> JSONArrayOfObjects<T> { | |
pub fn to_json(items : Vec<T>) -> Result<String, String> { | |
let mut json_vec = vec![]; | |
json_vec.push(SYMBOL.opening_square_bracket.to_string()); | |
for (pos, item) in items.iter().enumerate() { | |
json_vec.push(item.to_json_string()); | |
if pos != items.len() - 1 { | |
json_vec.push(SYMBOL.comma.to_string()); | |
json_vec.push(SYMBOL.new_line_carriage_return.to_string()); | |
} | |
} | |
json_vec.push(SYMBOL.closing_square_bracket.to_string()); | |
let result = json_vec.join(SYMBOL.empty_string); | |
Ok(result) | |
} | |
} | |
impl<T: FromJSON + New> JSONArrayOfObjects<T> { | |
pub fn from_json(json : String) -> Result<Vec<T>, String> { | |
let items = RawUnprocessedJSONArray::split_into_vector_of_strings(json).unwrap(); | |
let mut list: Vec<T> = vec![]; | |
for item in items { | |
let mut object = T::new(); | |
object.parse(item).unwrap(); | |
list.push(object); | |
} | |
Ok(list) | |
} | |
} | |
pub trait ToJSON { | |
fn list_properties() -> Vec<JSONProperty>; | |
fn get_property(&self, property_name: String) -> JSONValue; | |
fn to_json_string(&self) -> String; | |
} | |
pub trait FromJSON { | |
fn parse_json_to_properties(&self, json_string: String) -> Result<Vec<(JSONProperty, JSONValue)>, String>; | |
fn set_properties(&mut self, properties: Vec<(JSONProperty, JSONValue)>) -> Result<(), String>; | |
fn parse(&mut self, json_string: String) -> Result<(), String>; | |
} | |
// implementing generics, library user code | |
pub struct ExampleObject { | |
pub prop_a: String, | |
pub prop_b: bool, | |
pub prop_c: bool, | |
pub prop_d: i128, | |
pub prop_e: f64 | |
} | |
impl New for ExampleObject { | |
fn new() -> Self { | |
ExampleObject { | |
prop_a: "".to_string(), | |
prop_b: false, | |
prop_c: false, | |
prop_d: 0, | |
prop_e: 0.0, | |
} | |
} | |
} | |
impl FromJSON for ExampleObject { | |
fn parse_json_to_properties(&self, json_string: String) -> Result<Vec<(JSONProperty, JSONValue)>, String> { | |
let boxed_parse = JSON::parse_as_properties(json_string); | |
if boxed_parse.is_err() { | |
let message = boxed_parse.err().unwrap(); | |
return Err(message) | |
} | |
let properties = boxed_parse.unwrap(); | |
Ok(properties) | |
} | |
fn set_properties(&mut self, properties: Vec<(JSONProperty, JSONValue)>) -> Result<(), String> { | |
for (property, value) in properties { | |
if property.property_name == "prop_a" { | |
if value.string.is_some() { | |
self.prop_a = value.string.unwrap(); | |
} | |
} | |
if property.property_name == "prop_b" { | |
if value.bool.is_some() { | |
self.prop_b = value.bool.unwrap(); | |
} | |
} | |
if property.property_name == "prop_c" { | |
if value.bool.is_some() { | |
self.prop_c = value.bool.unwrap(); | |
} | |
} | |
if property.property_name == "prop_d" { | |
if value.i128.is_some() { | |
self.prop_d = value.i128.unwrap(); | |
} | |
} | |
if property.property_name == "prop_e" { | |
if value.f64.is_some() { | |
self.prop_e = value.f64.unwrap(); | |
} | |
} | |
} | |
Ok(()) | |
} | |
fn parse(&mut self, json_string: String) -> Result<(), String> { | |
let boxed_properties = self.parse_json_to_properties(json_string); | |
if boxed_properties.is_err() { | |
let message = boxed_properties.err().unwrap(); | |
return Err(message); | |
} | |
let properties = boxed_properties.unwrap(); | |
let boxed_set = self.set_properties(properties); | |
if boxed_set.is_err() { | |
let message = boxed_set.err().unwrap(); | |
return Err(message); | |
} | |
Ok(()) | |
} | |
} | |
impl ToJSON for ExampleObject { | |
fn list_properties() -> Vec<JSONProperty> { | |
let mut list = vec![]; | |
let property = JSONProperty { property_name: "prop_a".to_string(), property_type: JSON_TYPE.string.to_string() }; | |
list.push(property); | |
let property = JSONProperty { property_name: "prop_b".to_string(), property_type: JSON_TYPE.boolean.to_string() }; | |
list.push(property); | |
let property = JSONProperty { property_name: "prop_c".to_string(), property_type: JSON_TYPE.boolean.to_string() }; | |
list.push(property); | |
let property = JSONProperty { property_name: "prop_d".to_string(), property_type: JSON_TYPE.integer.to_string() }; | |
list.push(property); | |
let property = JSONProperty { property_name: "prop_e".to_string(), property_type: JSON_TYPE.number.to_string() }; | |
list.push(property); | |
list | |
} | |
fn get_property(&self, property_name: String) -> JSONValue { | |
let mut value = JSONValue::new(); | |
if property_name == "prop_a".to_string() { | |
let string : String = self.prop_a.to_owned(); | |
value.string = Some(string); | |
} | |
if property_name == "prop_b".to_string() { | |
let boolean : bool = self.prop_b; | |
value.bool = Some(boolean); | |
} | |
if property_name == "prop_c".to_string() { | |
let boolean : bool = self.prop_c; | |
value.bool = Some(boolean); | |
} | |
if property_name == "prop_d".to_string() { | |
let integer : i128 = self.prop_d; | |
value.i128 = Some(integer); | |
} | |
if property_name == "prop_e".to_string() { | |
let floating_point_number: f64 = self.prop_e; | |
value.f64 = Some(floating_point_number); | |
} | |
value | |
} | |
fn to_json_string(&self) -> String { | |
let mut processed_data = vec![]; | |
let properties = ExampleObject::list_properties(); | |
for property in properties { | |
let value = self.get_property(property.property_name.to_string()); | |
processed_data.push((property, value)); | |
} | |
JSON::to_json_string(processed_data) | |
} | |
} | |
// using generics, library user code, example 1 | |
let obj = ExampleObject::new(); | |
let obj2 = ExampleObject { | |
prop_a: "test".to_string(), | |
prop_b: true, | |
prop_c: false, | |
prop_d: 10, | |
prop_e: 2.2, | |
}; | |
let actual = JSONArrayOfObjects::<ExampleObject>::to_json(vec![obj, obj2]).unwrap(); | |
// using generics, library user code, example 2 | |
let json = "[{\r\n \"prop_a\": \"some text\",\r\n \"prop_b\": false}]".to_string(); | |
let parsed_list : Vec<ExampleObject> = JSONArrayOfObjects::<ExampleObject>::from_json(json).unwrap(); |
impl<T: New> JSONArrayOfObjects<T>
basically means that somewhere within JSONArrayOfObjects fileds or functions we are going to use specific type T which implements New trait
JSONArrayOfObjects::<ExampleObject>::to_json(vec![obj, obj2])
here JSONArrayOfObjects function to_json accepts as a parameter vector of any type that implements ToJSON trait (line 222)
JSONArrayOfObjects::<ExampleObject>::from_json(json)
here JSONArrayOfObjects function from_json returns result containing vector of any type that implements FromJSON and New traits (line 227)
Prefix JSONArrayOfObjects::<ExampleObject>
is where compiler being told explicitly to use ExampleObject as generic type
So, on one hand, there is a compiler that needs to guarantee type safety at compile time and on another hand there is a developer that wants to write extensible code.
JSONArrayOfObjects is designed to be used as a library, so it can accept a large variety of unknown types.
Generics is a way to allow usage of any type which fulfills specific requirements.
Use cases for generics: