use_state in Yew - yew

I am new to Rust and Yew and I need some assistance in applying a useState hook inside an app component in Yew that looks like this:
struct BookList
use gloo_net::http::Request;
use yew::prelude::*;
#[function_component(App)]
pub fn app() -> Html {
let (books: UseStateHandle<Vec<BookList>> = use_state(|| vec![]);
async fn fetch_books() {
let response: Vec<> = Request::get("http://localhost:3000/books");
let cloned_books = books.clone():
cloned_books.set(response.data);
}
html! {
<div>
{"App"}
</div>
}
}
I got the error mismatched types. Where I have the parens, it says it expected a struct UseStateHandle, found tuple.

I'm not sure why you need an open bracket before books but I assume that this is where the confusion with the tuple comes from.
let books = use_state(|| vec![]);
If you assign the type when you fetch the data I am speculating that Rust should be smart enough to understand from the later context that this should be a handle to a vector of BookList. Thus, I would assign the type there (which you already almost had but it was missing the type):
let response: Vec<BookList> = Request::get("http://localhost:3000/books");
Note that I have not tried these. Feel free to comment with follow-up exceptions so we can together elaborate.

Related

binary deserialization to another type with FSPickler

I have a type:
type T =
{
a: int
b: string
}
and an array of type T called 'myArray'.
and I want to serialize myArray with FSPickler:
FsPickler.CreateBinarySerializer().Pickle myArray
but in the pickle file, the type T is fully qualified and if I relocate it to another app/namespace, then the unpickle fails.
I asked on the FSPickler git but the answer I received is:
Per documentation in http://mbraceproject.github.io/FsPickler/ the serializer is not > designed with version tolerance in mind. That being said, it should be possible to work > around that issue by providing a custom implementation of the ITypeNameConverter interface.
ok, fair enough.
However, the documentation is providing examples clearly written by someone that knows picklers in general and geared toward other people that know picklers as well.
Can anyone post an example where I could make a custom serializer / deserializer for this basic case? From the docs, I see all the odd cases explained with the assumption that the reader knows how to make a basic serializer with FSPickler.
Or, maybe I missed something terribly obvious in the doc and I'd gladly recognize it if I saw it :)
The doc is here: https://mbraceproject.github.io/FsPickler/tutorial.html
and, for context, I'm going through 24 GB of data (with a type more complex than in this example obviously), so SPEED is everything and FSPickler seems quite fast.
There doesn't seem to be any documentation on this at all, but I managed to hack together something that works. The trick I found is to use a very simple converter that always uses the same type name, like this:
let converter =
{
new ITypeNameConverter with
member this.OfSerializedType(typeInfo) =
{ typeInfo with Name = "dummy" } // always use dummy type name
member this.ToDeserializedType(typeInfo) =
typeInfo
}
We can then pickle an array of Ts like this:
type T =
{
a: int
b: string
}
let tArray =
[|
{ a = 1; b = "one" }
{ a = 2; b = "two" }
{ a = 3; b = "three" }
|]
let serializer = FsPickler.CreateBinarySerializer(typeConverter = converter)
let bytes = serializer.Pickle(tArray)
And then we can unpickle it to an array of a different type that has the same structure ("duck typing"):
type U =
{
a: int
b: string
}
let uArray = serializer.UnPickle<U[]>(bytes)
I have no idea if this is the "right" way to do it. I found that ITypeNameConverter didn't behave like I expected. In particular, it seems that OfSerializedType is called during both pickling and unpickling, while ToDeserializedType isn't called at all. Maybe I'm missing something important.

IgnoreMissingMember setting doesn't seem to work with FSharpLu.Json deserializer

This is a following to: deserialization issue, with json.net, in F#.
I am deserializing some JSON that has an extra, unbound property using FSharpLu.Json. Here is the code:
open System
open Newtonsoft.Json
open Microsoft.FSharpLu.Json
type r =
{
a: int
}
let a =
"{\"a\":3, \"b\":5}"
Compact.TupleAsArraySettings.settings.MissingMemberHandling <- MissingMemberHandling.Ignore
Compact.deserialize<r> a // doesn't work
Despite setting MissingMemberHandling.Ignore it returns a json.net error:
Could not find member 'b' on object of type 'r'. Path 'b', line 1, position 13.
Is there a way to make this work, or is it an issue with FSharpLu.Json?
Here is the fiddle: https://dotnetfiddle.net/OsVv1M
as a side note, there is another deserializer in FSharpLu.Json and I can get that code to work with it:
FSharpLu.Json.Default.Internal.DefaultSettings.settings.MissingMemberHandling <- MissingMemberHandling.Ignore
Default.deserialize<r> a
will work, but that deserializer doesn't handle discriminated unions... so I need to get the compact one to work.
When looking into the source of FSharpLu.Json, I found this:
/// Compact serialization where tuples are serialized as heterogeneous arrays
type TupleAsArraySettings =
static member formatting = Formatting.Indented
static member settings =
JsonSerializerSettings(
NullValueHandling = NullValueHandling.Ignore,
// MissingMemberHandling is not technically needed for
// compact serialization but it avoids certain ambiguities
// that guarantee that deserialization coincides with the
// default Json.Net deserialization.
// (where 'coincides' means 'if the deserialization succeeds they both return the same object')
// This allows us to easily define the BackwardCompatible
// serializer (that handles both Compact and Default Json format) by reusing
// the Compact deserializer.
MissingMemberHandling = MissingMemberHandling.Error,
Converters = [| CompactUnionJsonConverter(true, true) |]
)
so they are explicitly setting the MissingMemberHandling to Error; maybe the solution is to instantiate the deserializer, change the setting and then use it.
The serializer setting you are trying to mutate, Compact.TupleAsArraySettings.settings, is a static member, as is shown in the code:
type TupleAsArraySettings =
static member formatting = Formatting.Indented
static member settings =
JsonSerializerSettings(
NullValueHandling = NullValueHandling.Ignore,
// MissingMemberHandling is not technically needed for
// compact serialization but it avoids certain ambiguities
// that guarantee that deserialization coincides with the
// default Json.Net deserialization.
// (where 'coincides' means 'if the deserialization succeeds they both return the same object')
// This allows us to easily define the BackwardCompatible
// serializer (that handles both Compact and Default Json format) by reusing
// the Compact deserializer.
MissingMemberHandling = MissingMemberHandling.Error,
Converters = [| CompactUnionJsonConverter(true, true) |]
)
Since a member is actually a member function (i.e. a method) as explained F# for fun and profit: Attaching functions to types, settings is actually (in c# terminology) a static property returning a new instance of JsonSerializerSettings each time it is invoked. To test this, we can do:
printfn "%b" (Object.ReferenceEquals(Compact.TupleAsArraySettings.settings, Compact.TupleAsArraySettings.settings)) // prints "false"
Which prints "false". Thus mutating the returned value has no effect on the behavior of Compact. A static field in the c# sense would be defined by a static let statement; if settings returned such a field, mutating its contents would have had an effect.
In any event modifying the value of Compact.TupleAsArraySettings.settings.MissingMemberHandling seems unwise as doing so would modify the behavior of of Compact.deserialize throughout your entire AppDomain in a way that might break backwards compatibility with Json.NET native serialization. As explained in the code comments above, the setting is required to make BackwardCompatible.deserialize work. But why might that be? Since Json.NET's native format for option and discriminated unions looks like:
{
"a": {
"Case": "Some",
"Fields": [
3
]
}
}
We can guess that MissingMemberHandling is used to trap situations where "Case" and "Fields" are found or not, and switch from one algorithm to the other.
If you are certain you do not need to deserialize f# types in Json.NET format, it seems you should be able to use CompactUnionJsonConverter directly as follows:
let settings = JsonSerializerSettings(
NullValueHandling = NullValueHandling.Ignore,
Converters = [| CompactUnionJsonConverter(true, true) |]
)
let c = JsonConvert.DeserializeObject<r>(a, settings)
let json2 = JsonConvert.SerializeObject(c, Formatting.Indented, settings)
Demo fiddle here.

Error "nil requires a contextual type" using Swift

I'd like to achieve the following in code:
class MyService {
let mySubject = BehaviorSubject<MyData>(value: nil)
//....
}
Unfortunately, I get the "nil requires a contextual type" error. I want the subject to be "empty" till I actually put something in there. How can I pass nil as the argument then? Can I cast it to my own type to make it work?
Based on the reference for RxSwift BehaviorSubject, the init(value:) initializer is declared as
public init(value: Element)
Where the value parameter is described as:
value
Initial value sent to observers when no other value has been received
by the subject yet.
And where Element is the placeholder type of BehaviorSubject:
public final class BehaviorSubject<Element> ...
This means you need to specify the placeholder type Element as an Optional type if you are to be able to set the initial value (used when no other value has been received) to nil. E.g.:
class MyService {
let mySubject = BehaviorSubject<MyData?>(value: nil)
//....
}
Or, letting the compiler infer the placeholder as MyData? by using the non-sugared .none form for the nil argument:
class MyService {
let mySubject = BehaviorSubject(value: Optional<MyData>.none)
//....
}
As for understanding the actual error message better, consider the following self-contained example:
struct Foo<T> {
init(value: T) {}
}
struct Bar {}
let bar = Bar()
_ = Foo<Bar>(value: bar) // OK
_ = Foo(value: bar) // OK, T inferred as Bar
_ = Foo<Bar>(value: nil) // Error: error: 'nil' requires a contextual type
_ = Foo<Bar?>(value: nil) // OK
_ = Foo(value: Optional<Bar>.none) // OK, T inferred as Bar?
While dfri's answer is technically correct, you might want to consider a different type when working with RxSwift. Since you want your subject to be empty only at the beginning, I'd suggest to use ReplaySubject or PublishSubject.
A similar question has also been asked on RxSwift's GitHub issue page. Allow BehaviorSubject without initial value. There, kzaher suggests the ReplaySubject.
Your subject would then look like this, without any initial value and without MyData being Optional.
let subject = ReplaySubject<MyData>().create(bufferSize: 1)

Extracting a file extension from a given path in Rust idiomatically

I am trying to extract the extension of a file from a given String path.
The following piece of code works, but I was wondering if there is a cleaner and more idiomatic Rust way to achieve this:
use std::path::Path;
fn main() {
fn get_extension_from_filename(filename: String) -> String {
//Change it to a canonical file path.
let path = Path::new(&filename).canonicalize().expect(
"Expecting an existing filename",
);
let filepath = path.to_str();
let name = filepath.unwrap().split('/');
let names: Vec<&str> = name.collect();
let extension = names.last().expect("File extension can not be read.");
let extens: Vec<&str> = extension.split(".").collect();
extens[1..(extens.len())].join(".").to_string()
}
assert_eq!(get_extension_from_filename("abc.tar.gz".to_string()) ,"tar.gz" );
assert_eq!(get_extension_from_filename("abc..gz".to_string()) ,".gz" );
assert_eq!(get_extension_from_filename("abc.gz".to_string()) , "gz");
}
In idiomatic Rust the return type of a function that can fail should be an Option or a Result. In general, functions should also accept slices instead of Strings and only create a new String where necessary. This reduces excessive copying and heap allocations.
You can use the provided extension() method and then convert the resulting OsStr to a &str:
use std::path::Path;
use std::ffi::OsStr;
fn get_extension_from_filename(filename: &str) -> Option<&str> {
Path::new(filename)
.extension()
.and_then(OsStr::to_str)
}
assert_eq!(get_extension_from_filename("abc.gz"), Some("gz"));
Using and_then is convenient here because it means you don't have to unwrap the Option<&OsStr> returned by extension() and deal with the possibility of it being None before calling to_str. I also could have used a lambda |s| s.to_str() instead of OsStr::to_str - it might be a matter of preference or opinion as to which is more idiomatic.
Notice that both the argument &str and the return value are references to the original string slice created for the assertion. The returned slice cannot outlive the original slice that it is referencing, so you may need to create an owned String from this result if you need it to last longer.
What's more idiomatic than using Rust's builtin method for it?
Path::new(&filename).extension()

Type Error calling closures stored in struct

I've recently started trying to write some toy programs in rust, and I guess I wanted to take a stab at a parser combinator.
My naive attempt was this:
enum ParseResult<T> {
Ok(T),
Error (String),
}
struct Parser<'a, I,O>{
Parse: |I|:'a ->ParseResult<O>,
}
impl<'a, I,O,J> Parser<'a, I, O>{
fn Compose<I,O,K>(self, RHS : Parser<O,K> )->Parser<I,K>{
Parser
{
Parse : |x:I|
{
let intermediate = self.Parse;
match intermediate(x){ //mismatched types: expected `I` but found `I` (expected type parameter but found type parameter)
Ok(result) =>
{
let final = RHS.Parse;
final(result) mismatched types: expected `O` but found `0` (expected type parameter but found type parameter)
}
Error(result) => Error(result)
}
}
}
}
}
I'm pretty sure the Errors indicate something more fundamentally wrong with my approach but I'm not quite sure what.
Indeed, there is a couple of errors in your program.
Currently closures are stack-"boxed", so you can't return them from functions, and that's exactly what happening in your example - you're storing a closure in Parser struct.
Another error is incorrect usage of generic parameters. Even without closures your method wouldn't work: I and O parameters on Compose method are different from those on impl clause, even if they share the same name.
BTW, you don't need to move a closure into intermediate variable. To call closures stored in fields you need to do this:
let intermediate = (self.Parse)(x);
That said, several days ago unboxed closures finally landed. With them it is possible to do what you want, though it is somewhat cumbersome at the moment.
#![feature(unboxed_closures)]
struct Parser<I, O> {
parse: Box<FnMut<(I,), Result<O, String>>>
}
impl<I, O> Parser<I, O> {
fn compose<K>(mut self, mut rhs: Parser<O, K>) -> Parser<I, K> {
Parser {
parse: box |&mut: x: I| {
match self.parse.call_mut((x,)) {
Ok(r) => rhs.parse.call_mut((r,)),
Err(e) => Err(e)
}
}
}
}
}
This should work but right now it doesn't, and it looks like a bug to me:
<anon>:11:23: 11:33 error: cannot borrow data mutably in an aliasable location
<anon>:11 match self.parse.call_mut((x,)) {
^~~~~~~~~~
<anon>:12:35: 12:44 error: cannot borrow data mutably in an aliasable location
<anon>:12 Ok(result) => rhs.parse.call_mut((result,)),
^~~~~~~~~
When I rewritten this example with manually expanded unboxed closure it worked, so there's definitely some compiler problem here. Unboxed closures are very new, so such things are not unexpected. I've submitted an issue on this.

Resources