[Best practice for optional in proto or default value - rust

We are setting up our rust services and using prost-build to bridge between proto <-> rust land. Our proto definitions are in proto3
Lets take the following proto message:
message Test {
string id = 1;
string body = 2;
string maybe_nullable_thing = 3;
}
This generates a struct like so:
pub struct Test {
#[prost(string, tag="1")]
pub id: ::prost::alloc::string::String,
#[prost(string, tag="2")]
pub body: ::prost::alloc::string::String,
#[prost(string, tag="3")]
pub maybe_nullable_thing: ::prost::alloc::string::String,
}
In other languagues where we have tried this, the fields of a proto message are optional by design and can be left out. In the example there can be cases where maybe_nullable_thing can not be set.
I can work around this issue by using the optional keyword. Altho I remember that it was not the best practice to do so(maybe I am mistaken?)
In terms of best practice with proto3 and rust in general, is it okay to use the optional keyword? If I use serde along with my Test struct I can see the default values of all the fields begin set to "".to_owned() (or empty string).
So I am not sure whats the best practice here? Would love to get some pointers on the best way forward here.

Looking at the readme for Tokio's PROST! tool, it appears their advice is to wrap any non-repeated and non-scalar fields, or any optional fields with Option<T>. This may or may not be different for prost-build, but it should give you a good reference to what's expected when using proto3 and Rust.
In general, however, you should wrap any value you want to be optional in Option<T>. This is not a bad practice, this is the default, standard way to represent "maybe nullable things" in Rust.

Related

NEP-141 implementation

While trying to implement NEP-141 fungible token, I am using trait
impl FungibleTokenCore for FungibleToken {
fn ft_transfer(&mut self, receiver_id: ValidAccountId, amount: U128, memo: Option<String>) {
assert_one_yocto();
let sender_id = env::predecessor_account_id();
let amount: Balance = amount.into();
self.internal_transfer(&sender_id, receiver_id.as_ref(), amount, memo);
}
}
But the problem is the function ft_transfer is inaccessible from the contract. It gives error:
"Contract method is not found".
export TOKEN=dev-1618119753426-1904392
near call $TOKEN ft_transfer '{"receiver_id":"avrit.testnet", "amount": 10, "memo":""}' --accountId=amiyatulu.testnet
Your method must be public. See the near-sdk-rs docs README for a few examples.
https://github.com/near/near-sdk-rs
You probably need a pub before that fn. See Best Practices.
Also see FT example. You can use the near-contract-standards library to simplify your efforts.
As you mentioned in a comment, the problem is that you need to:
Use #[near_bindgen] for the impl definition:
#[near_bindgen]
impl FungibleTokenCore for FungibleToken { ... }
Use a public method:
pub fn ft_transfer(&mut self, ...)
There is already an implementation of this token in near-contract-standards
I am unable to add custom logic into the token using the library, so not using it.
Regarding how to extend the behaviour of the toke I suggest you to take a look at how the Rainbow Bridge does it for ERC20 tokens bridged to Near: BridgeToken. We also needed to extend its functionalities, and for this end, we used the Token as an internal field, and then changed a bit the public functions exposed.
There is also a useful macro to derive base implementation for common functions.
Like burning some tokens during transfer.
To this, you can follow the previous approach, without using the macro to expose all functions, and instead implement ft_transfer properly for your use case, but still making calls to the inner field: token: FungibleToken.

How can I make this Rust code more idiomatic

Recently I started to learn Rust and one of my main struggles is converting years of Object Oriented thinking into procedural code.
I'm trying to parse a XML that have tags that are processed by an specific handler that can deal with the data it gets from the children.
Further more I have some field members that are common between them and I would prefer not to have to write the same fields to all the handlers.
I tried my hand on it and my code came out like this:
use roxmltree::Node; // roxmltree = "0.14.0"
fn get_data_from(node: &Node) -> String {
let tag_name = get_node_name(node);
let tag_handler: dyn XMLTagHandler = match tag_name {
"name" => NameHandler::new(),
"phone" => PhoneHandler::new(),
_ => DefaultHandler::new()
}
if tag_handler.is_recursive() {
for child in node.children() {
let child_value = get_data_from(&child);
// do something with child value
}
}
let value: String = tag_handler.value()
value
}
// consider that handlers are on my project and can be adapted to my needs, and that XMLTagHandler is the trait that they share in common.
My main issues with this are:
This feels like a Object oriented approach to it;
is_recursive needs to be reimplemented to each struct because they traits cannot have field members, and I will have to add more fields later, which means more boilerplate for each new field;
I could use one type for a Handler and pass to it a function pointer, but this approach seems dirty. e.g.:=> Handler::new(my_other_params, phone_handler_func)
This feels like a Object oriented approach to it
Actually, I don't think so. This code is in clear violation of the Tell-Don't-Ask principle, which falls out from the central idea of object-oriented programming: the encapsulation of data and related behavior into objects. The objects (NameHandler, PhoneHandler, etc.) don't have enough knowledge about what they are to do things on their own, so get_data_from has to query them for information and decide what to do, rather than simply sending a message and letting the object figure out how to deal with it.
So let's start by moving the knowledge about what to do with each kind of tag into the handler itself:
trait XmlTagHandler {
fn foreach_child<F: FnMut(&Node)>(&self, node: &Node, callback: F);
}
impl XmlTagHandler for NameHandler {
fn foreach_child<F: FnMut(&Node)>(&self, _node: &Node, _callback: F) {
// "name" is not a recursive tag, so do nothing
}
}
impl XmlTagHandler for DefaultHandler {
fn foreach_child<F: FnMut(&Node)>(&self, node: &Node, callback: F) {
// all other tags may be recursive
for child in node.children() {
callback(child);
}
}
}
This way you call foreach_child on every kind of Handler, and let the handler itself decide whether the right action is to recurse or not. After all, that's why they have different types -- right?
To get rid of the dyn part, which is unnecessary, let's write a little generic helper function that uses XmlTagHandler to handle one specific kind of tag, and modify get_data_from so it just dispatches to the correct parameterized version of it. (I'll suppose that XmlTagHandler also has a new function so that you can create one generically.)
fn handle_tag<H: XmlTagHandler>(node: &Node) -> String {
let handler = H::new();
handler.foreach_child(node, |child| {
// do something with child value
});
handler.value()
}
fn get_data_from(node: &Node) -> String {
let tag_name = get_node_name(node);
match tag_name {
"name" => handle_tag::<NameHandler>(node),
"phone" => handle_tag::<PhoneHandler>(node),
_ => handle_tag::<DefaultHandler>(node),
}
}
If you don't like handle_tag::<SomeHandler>(node), also consider making handle_tag a provided method of XmlTagHandler, so you can instead write SomeHandler::handle(node).
Note that I have not really changed any of the data structures. Your presumption of an XmlTagHandler trait and various Handler implementors is a pretty normal way to organize code. However, in this case, it doesn't offer any real improvement over just writing three separate functions:
fn get_data_from(node: &Node) -> String {
let tag_name = get_node_name(node);
match tag_name {
"name" => get_name_from(node),
"phone" => get_phone_from(node),
_ => get_other_from(node),
}
}
In some languages, such as Java, all code has to be part of some class – so you can find yourself writing classes that don't exist for any other reason than to group related things together. In Rust you don't need to do this, so make sure that any added complication such as XmlTagHandler is actually pulling its weight.
is_recursive needs to be reimplemented to each struct because they traits cannot have field members, and I will have to add more fields later, which means more boilerplate for each new field
Without more information about the fields, it's impossible to really understand what problem you're facing here; however, in general, if there is a family of structs that have some data in common, you may want to make a generic struct instead of a trait. See the answers to How to reuse codes for Binary Search Tree, Red-Black Tree, and AVL Tree? for more suggestions.
I could use one type for a Handler and pass to it a function pointer, but this approach seems dirty
Elegance is sometimes a useful thing, but it is subjective. I would recommend closures rather than function pointers, but this suggestion doesn't seem "dirty" to me. Making closures and putting them in data structures is a very normal way to write Rust code. If you can elaborate on what you don't like about it, perhaps someone could point out ways to improve it.

Deserializing a String with into_serde makes the app panick

With a friend of mine, we're trying to use the serde_json crate to deserialize some message sent by a WebSocket.
We are having a specific error, and we managed to recreate it with the following snippet of code:
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Debug)]
struct EffetSer {
test: String
}
fn main() {
let test_value = JsValue::from_str("{\"test\": \"value\"}");
let test_value: EffetSer = test_value.into_serde().unwrap();
log::error!("WOW : {:?}", test_value);
}
Our TOML has the following dependencies:
wasm-bindgen = { version = '0.2.63', features = ['serde-serialize'] }
serde = { version = '1.0', features = ["derive"] }
serde_json = '1.0.55'
js-sys = '0.3.40'
The error is the following:
app.js:310 panicked at 'called `Result::unwrap()` on an `Err` value: Error("invalid type: string \"{\\\"test\\\": \\\"value\\\"}\", expected struct EffetSer", line: 1, column: 23)'
Any help would be very appreciated, as we're still struggling to understand what we're doing wrong and why we cannot deserialize our String.
The problem is likely misunderstanding of into_serde's semantics.
According to documentation, it works like this:
Invokes JSON.stringify on this value and then parses the resulting JSON into an arbitrary Rust value.
In other words, its semantics are as following:
convert each component of the JsValue to the corresponding serde internal element;
deserialize the required type from the given tree of components.
Now, what does this mean in our case? Well, you created JsValue using JsValue::from_str, which, again according to documentation,
Creates a new JS value which is a string.
So, the JsValue here is not an object, as you are likely assuming; it is a primitive - a string, which simply happens to have the shape of object's JSON representation. Then, when you invoke from_serde, Serde sees the string - not as input, but as internal representation, which cannot be transformed into object.
Now, what to do? There are several ways to fix this code:
First and the most obvious: don't use JsValue at all, deserialize from &str directly with serde_json::from_str.
Use js_sys::JSON::parse to get the object-like JsValue from string, and then convert it to the EffetSer with into_serde. This is likely to be less efficient, since it requires the round-trip of JSON::parse and JSON::serialize to convert the string to object and then back to string.
Write your own method to convert JsValue to EffetSer directly. I'm not sure if this is possible, however, since I wasn't able to find a way to extract a single field from JS object.

How to implement a struct in Rust that has a list of itself as a field

I have started out learning Rust and is currently trying to write a small neural network as personal exercise. I want to define a struct for my forthcoming Layers/Clusters/Groups of nodes. My initial definition looks like this:
struct Layer {
name: String, // Human readable name
id: String, // UUID in the future
order: u8, // int for sorting
width: u8, // Number of nodes
input: [&'Self], // References to other Layers that feed input into this
}
The thing I am struggling with is the input field which should contain a list of references to other Layer-instances. I will know at compile time how many each Layer will have in the list so it wont have to me mutable. Is it possible to do this? I cant find a solution on the Google machine or in "the book".
Please advise.
Is it possible to do this? I cant find a solution on the Google machine or in "the book".
Possible yes, though I would not recommend it.
Let's start with the possible: &Self would be a "layer reference" with an unnamed lifetime, a lifetime name is for the form '<symbol>, so when you write &'Self you're specifying a reference of lifetime 'Self, but you're never specifying the type being refered to, which is why rustc complains about "expected type".
If you add a "proper" lifetime name, and parametrize the structure, it compiles fine:
struct Layer<'sublayers> {
name: String, // Human readable name
id: String, // UUID in the future
order: u8, // int for sorting
width: u8, // Number of nodes
input: [&'sublayers Self], // References to other Layers that feed input into this
}
However I would not recommend it as the last member being a slice means it's a DST which are difficult to work with at the best of time -- as the nomicon specifically notes "custom DSTs are a largely half-baked feature for now".
Since Rust doesn't yet have const generics proper you can't use an array you'd parameterize through layer either (e.g. Layer<const Size> and input: [&Self;Size], maybe one day), so you probably want something like a vector or a slice reference e.g.
struct Layer<'slice, 'sublayers: 'slice> {
name: String, // Human readable name
id: String, // UUID in the future
order: u8, // int for sorting
width: u8, // Number of nodes
input: &'slice [&'sublayers Self], // References to other Layers that feed input into this
}

How to pass a dynamic amount of typed arguments to a function?

Lets say I want to write a little client for an HTTP API. It has a resource that returns a list of cars:
GET /cars
It also accepts the two optional query parameters color and manufacturer, so I could query specific cars like:
GET /cars?color=black
GET /cars?manufacturer=BMW
GET /cars?color=green&manufacturer=VW
How would I expose these resources properly in Rust? Since Rust doesn't support overloading, defining multiple functions seems to be the usual approach, like:
fn get_cars() -> Cars
fn get_cars_by_color(color: Color) -> Cars
fn get_cars_by_manufacturer(manufacturer: Manufacturer) -> Cars
fn get_cars_by_manufacturer_and_color(manufacturer: Manufacturer, color: Color) -> Cars
But this will obviously not scale when you have more than a few parameters.
Another way would be to use a struct:
struct Parameters {
color: Option<Color>,
manufacturer: Option<Manufacturer>
}
fn get_cars(params: Parameters) -> Cars
This has the same scaling issue, every struct field must be set on creation (even if its value is just None).
I guess I could just accept a HashMap<String, String>, but that doesn't sound very good either.
So my question is, what is the proper/best way to do this in Rust?
You could use the Builder pattern, as mentioned here. For your particular API, it could look like this:
Cars::new_get()
.by_color("black")
.by_manufacturer("BMW")
.exec();
I would like to point out that no matter the solution, if you wish for a compile-time checked solution the "url parsing -> compile-time checkable" translation is necessarily hard-wired. You can generate that with an external script, with macros, etc... but in any case for the compiler to check it, it must exist at compile-time. There just is no short-cut.
Therefore, no matter which API you go for, at some point you will have something akin to:
fn parse_url(url: &str) -> Parameters {
let mut p: Parameters = { None, None };
if let Some(manufacturer) = extract("manufacturer", url) {
p.manufacturer = Some(Manufacturer::new(manufacturer));
}
if let Some(color) = extract("color", url) {
p.color = Some(Color::new(color));
}
p
}
And although you can try and sugarcoat it, the fundamentals won't change.

Resources