async syntactic oddity - rust

Why does Rust allow this:
fn main() {
let f = |x: bool| {
async {
return
}
};
}
But not this? Specifically, the error complains that the branches return different types, when they appear to be exactly the same.
fn main() {
let f = |x: bool| {
if true {
async {
return
}
} else {
async {
return
}
}
};
}
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:42:13
|
37 | / if true {
38 | | async {
| _|_____________-
39 | | | return
40 | | | }
| |_|_____________- expected because of this
41 | | } else {
42 | / | async {
43 | | | return
44 | | | }
| |_|_____________^ expected `async` block, found a different `async` block
45 | | }
| |_________- `if` and `else` have incompatible types
|

Every time you write async { }, the compiler makes a unique anonymous Future type. They will be distinct even if two are syntactically equivalent. This is the same for closures.
So your first snippet is simply returning an object (with an anonymous type), while your second is trying to return different types conditionally, which is not allowed. Consider using a trait object so they are the same type:
use std::future::Future;
fn main() {
let f = |x: bool| {
if true {
Box::new(async {
return
}) as Box<dyn Future<Output=()>>
} else {
Box::new(async {
return
})
}
};
}

Related

Writing tests for nom parsers with VerboseError and convert_error

I'm trying to write tests for nom parsers that provide better messages when they fail.
I have a macro similar to assert_eq! called assert_parses_to! that I want to provide a user-friendly display string when the parser fails:
#[macro_export]
macro_rules! assert_parses_to {
($parser:ident, $input:expr, $expectation:expr) => {
match (&$parser($input), &$expectation) {
(Err(candidate_error), expectation_val) => {
panic!(
"Failed to parse the candidate\n\
{}",
convert_error($input, *candidate_error)
);
}
(Ok(candidate_val), expectation_val) => {
if !(*candidate_val == *expectation_val) {
panic!(
"Failed to parse to expected value\n\
Got: {:?}\n\
Expected: {:?}",
candidate_val, expectation_val
)
}
}
}
};
}
However, when trying to compile, I get this error:
error[E0308]: mismatched types
--> src/parser/tests.rs:27:43
|
27 | convert_error($input, *candidate_error)
| ------------- ^^^^^^^^^^^^^^^^ expected struct `VerboseError`, found enum `nom::Err`
| |
| arguments to this function are incorrect
|
::: src/parser/ansi_2016/lexical.rs:237:9
|
237 | / assert_parses_to!(
238 | | regular_identifier,
239 | | "identifier ",
240 | | (
... |
246 | | )
247 | | )
| |_________- in this macro invocation
|
= note: expected struct `VerboseError<&str>`
found enum `nom::Err<VerboseError<&str>>`
I'm assuming that I need to modify the match statement to extract the VerboseError from the error, but I haven't figured out how.
I'm new to rust and nom - any help appreciated!
Resolved by matching on the inner value of the Err enum:
#[macro_export]
macro_rules! assert_parses_to {
($parser:ident, $input:expr, $expectation:expr) => {
match ($parser($input), $expectation) {
// Deeper match here
(Err(::nom::Err::Error(e) | ::nom::Err::Failure(e)), _) => {
panic!("{}", ::nom::error::convert_error($input, e));
}
(Err(e), _) => {
panic!("Failed to parse {:?}", e)
}
(Ok(candidate_val), expected_val) => {
if !(candidate_val == expected_val) {
panic!(
"Failed to parse to expected value\n\
Got: {:?}\n\
Expected: {:?}",
candidate_val, expected_val
)
}
}
}
};
}

How to return string value of state in hook?

Returning string state in use_effect_with_deps gives error.
use std::ops::Deref;
use yew::prelude::*;
#[hook]
pub fn use_hook_test() -> String
{
let first_load = use_state(|| true);
let hash_state = use_state(|| "".to_owned());
let hash_state_clone = hash_state.clone();
use_effect_with_deps(move |_| {
if *first_load {
wasm_bindgen_futures::spawn_local(async move {
hash_state_clone.set(format!("{:?}", "Hello"));
});
first_load.set(false);
}
|| {};
}, ());
hash_state_clone.deref().clone()
}
Error:
let hash_state_clone = hash_state.clone();
| ---------------- move occurs because `hash_state_clone` has type `yew::UseStateHandle<std::string::String>`, which does not implement the `Copy` trait
14 | use_effect_with_deps(move |_| {
| -------- value moved into closure here
...
18 | hash_state_clone.set(format!("{:?}", "Hello"));
| ---------------- variable moved due to use in closure
...
27 | hash_state_clone.deref().clone()
| ^^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move
Here is a Yew Playground based on your example with some minor changes:
added an explicit scope to isolate the use_effect_with_deps
added a second hash_state.clone() after that scope
The result is somewhat nonsensical but compiles ok.
#[hook]
pub fn use_hook_test() -> String
{
let first_load = use_state(|| true);
let hash_state = use_state(|| "".to_owned());
{
let hash_state_clone = hash_state.clone();
use_effect_with_deps(move |_| {
if *first_load {
wasm_bindgen_futures::spawn_local(async move {
hash_state_clone.set(format!("{:?}", "Hello"));
});
first_load.set(false);
}
|| {};
}, ());
}
let hash_state_clone = hash_state.clone();
hash_state_clone.deref().clone()
}

Obtain a reference from a RefCell<Option<Rc<T>>> in Rust

I am having problem getting a reference out of a RefCell<Option<Rc>>.
Any suggestion?
struct Node<T> {
value: T
}
struct Consumer3<T> {
tail: RefCell<Option<Rc<Node<T>>>>,
}
impl<T> Consumer3<T> {
fn read<'s>(&'s self) -> Ref<Option<T>> {
Ref::map(self.tail.borrow(), |f| {
f.map(|s| {
let v = s.as_ref();
v.value
})
})
}
}
Gives:
error[E0308]: mismatched types
--> src/lib.rs:15:13
|
15 | / f.map(|s| {
16 | | let v = s.as_ref();
17 | | v.value
18 | | })
| |______________^ expected reference, found enum `Option`
|
= note: expected reference `&_`
found enum `Option<T>`
help: consider borrowing here
|
15 | &f.map(|s| {
16 | let v = s.as_ref();
17 | v.value
18 | })
|
error: aborting due to previous error
Playground
Mapping from one Ref to another requires that the target already exist in memory somewhere. So you can't get a Ref<Option<T>> from a RefCell<Option<Rc<Node<T>>>>, because there's no Option<T> anywhere in memory.
However, if the Option is Some, then there will be a T in memory from which you can obtain a Ref<T>; if the Option is None, obviously you can't. So returning Option<Ref<T>> may be a viable alternative for you:
use std::{cell::{Ref, RefCell}, rc::Rc};
struct Node<T> {
value: T
}
struct Consumer3<T> {
tail: RefCell<Option<Rc<Node<T>>>>,
}
impl<T> Consumer3<T> {
fn read(&self) -> Option<Ref<T>> {
let tail = self.tail.borrow();
if tail.is_some() {
Some(Ref::map(tail, |tail| {
let node = tail.as_deref().unwrap();
&node.value
}))
} else {
None
}
}
}
Playground.

How can I use Rust with wasm-bindgen to create a closure that creates another closure with state?

I am trying to create a small web application that will allow the user to drag and drop files onto the window. The files will then be read and their contents printed along with their filenames to the console. In addition, the files will be added to a list.
The equivalent code in JS could look something like:
window.ondragenter = (e) => {
e.preventDefault();
}
window.ondragover = (e) => {
e.preventDefault();
}
const allFiles = [];
const dropCallback = (e) => {
e.preventDefault();
const files = e.dataTransfer.files;
console.log("Got", files.length, "files");
for (let i = 0; i < files.length; i++) {
const file = files.item(i);
const fileName = file.name;
const readCallback = (text) => {
console.log(fileName, text);
allFiles.push({fileName, text});
}
file.text().then(readCallback);
}
};
window.ondrop = dropCallback;
When trying to do this in Rust, I run in to the problem that the outer closure needs to implement FnOnce to move all_files out of its scope again, which breaks the expected signature for Closure::wrap. And Closure::once will not do the trick, since I need to be able to drop multiple files onto the window.
Here is the code that I have tried without luck:
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen::JsValue;
macro_rules! console_log {
($($t:tt)*) => (web_sys::console::log_1(&JsValue::from(format_args!($($t)*).to_string())))
}
struct File {
name: String,
contents: String,
}
#[wasm_bindgen]
pub fn main() {
let mut all_files = Vec::new();
let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
event.prevent_default();
let drag_event_ref: &web_sys::DragEvent = JsCast::unchecked_from_js_ref(event);
let drag_event = drag_event_ref.clone();
match drag_event.data_transfer() {
None => {}
Some(data_transfer) => match data_transfer.files() {
None => {}
Some(files) => {
console_log!("Got {:?} files", files.length());
for i in 0..files.length() {
if let Some(file) = files.item(i) {
let name = file.name();
let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
let contents = text.as_string().unwrap();
console_log!("Contents of {:?} are {:?}", name, contents);
all_files.push(File {
name,
contents
});
}) as Box<dyn FnMut(JsValue)>);
file.text().then(&read_callback);
read_callback.forget();
}
}
}
},
}
}) as Box<dyn FnMut(&web_sys::Event)>);
// These are just necessary to make sure the drop event is sent
let drag_enter = Closure::wrap(Box::new(|event: &web_sys::Event| {
event.prevent_default();
console_log!("Drag enter!");
}) as Box<dyn FnMut(&web_sys::Event)>);
let drag_over = Closure::wrap(Box::new(|event: &web_sys::Event| {
event.prevent_default();
console_log!("Drag over!");
}) as Box<dyn FnMut(&web_sys::Event)>);
// Register all the events on the window
web_sys::window()
.and_then(|win| {
win.set_ondragenter(Some(JsCast::unchecked_from_js_ref(drag_enter.as_ref())));
win.set_ondragover(Some(JsCast::unchecked_from_js_ref(drag_over.as_ref())));
win.set_ondrop(Some(JsCast::unchecked_from_js_ref(drop_callback.as_ref())));
win.document()
})
.expect("Could not find window");
// Make sure our closures outlive this function
drag_enter.forget();
drag_over.forget();
drop_callback.forget();
}
The error I get is
error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
--> src/lib.rs:33:72
|
33 | ... let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
| - ^^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
| _________________________________________________________|
| |
34 | | ... let contents = text.as_string().unwrap();
35 | | ... console_log!("Contents of {:?} are {:?}", name, contents);
36 | | ...
37 | | ... all_files.push(File {
38 | | ... name,
| | ---- closure is `FnOnce` because it moves the variable `name` out of its environment
39 | | ... contents
40 | | ... });
41 | | ... }) as Box<dyn FnMut(JsValue)>);
| |________________________- the requirement to implement `FnMut` derives from here
error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
--> src/lib.rs:20:48
|
20 | let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
| - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
| _______________________________________|
| |
21 | | event.prevent_default();
22 | | let drag_event_ref: &web_sys::DragEvent = JsCast::unchecked_from_js_ref(event);
23 | | let drag_event = drag_event_ref.clone();
... |
33 | | let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
| | -------------------- closure is `FnOnce` because it moves the variable `all_files` out of its environment
... |
50 | | }
51 | | }) as Box<dyn FnMut(&web_sys::Event)>);
| |______- the requirement to implement `FnMut` derives from here
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0525`.
error: could not compile `hello_world`.
To learn more, run the command again with --verbose.
In a more complex example that I have not been able to reproduce in a simpler form, I get a more cryptic error, but I expect it to be related to the above:
error[E0277]: expected a `std::ops::FnMut<(&web_sys::Event,)>` closure, found `[closure#src/main.rs:621:52: 649:10 contents:std::option::Option<std::string::String>, drop_proxy:winit::event_loop::EventLoopProxy<CustomEvent>]`
--> src/main.rs:621:43
|
621 | let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
| ___________________________________________^
622 | | event.prevent_default();
623 | | let drag_event_ref: &web_sys::DragEvent = JsCast::unchecked_from_js_ref(event);
624 | | let drag_event = drag_event_ref.clone();
... |
648 | | }
649 | | }) as Box<dyn FnMut(&web_sys::Event)>);
| |__________^ expected an `FnMut<(&web_sys::Event,)>` closure, found `[closure#src/main.rs:621:52: 649:10 contents:std::option::Option<std::string::String>, drop_proxy:winit::event_loop::EventLoopProxy<CustomEvent>]`
I tried putting the all_files variable into a RefCell, but I still got a similar error. Are there any tricks or types that I can use to work around this in Rust and achieve what I want?
First, you are trying to copy name into a number of instances of File, but it must be cloned. Second, you need to properly ensure that all_files will be available whenever a closure wants to call it. One way to do so is by using a RefCell to enable multiple closures to write to it, and wrapping that in a Rc to ensure that it stays alive as long as any of the closures are alive.
Try this:
use std::{cell::RefCell, rc::Rc};
use wasm_bindgen::{prelude::*, JsCast, JsValue};
macro_rules! console_log {
($($t:tt)*) => (web_sys::console::log_1(&JsValue::from(format_args!($($t)*).to_string())))
}
struct File {
name: String,
contents: String,
}
#[wasm_bindgen]
pub fn main() {
let all_files = Rc::new(RefCell::new(Vec::new()));
let drop_callback = Closure::wrap(Box::new(move |event: &web_sys::Event| {
event.prevent_default();
let drag_event_ref: &web_sys::DragEvent = event.unchecked_ref();
let drag_event = drag_event_ref.clone();
match drag_event.data_transfer() {
None => {}
Some(data_transfer) => match data_transfer.files() {
None => {}
Some(files) => {
console_log!("Got {:?} files", files.length());
for i in 0..files.length() {
if let Some(file) = files.item(i) {
let name = file.name();
let all_files_ref = Rc::clone(&all_files);
let read_callback = Closure::wrap(Box::new(move |text: JsValue| {
let contents = text.as_string().unwrap();
console_log!("Contents of {:?} are {:?}", &name, contents);
(*all_files_ref).borrow_mut().push(File {
name: name.clone(),
contents,
});
})
as Box<dyn FnMut(JsValue)>);
file.text().then(&read_callback);
read_callback.forget();
}
}
}
},
}
}) as Box<dyn FnMut(&web_sys::Event)>);
// These are just necessary to make sure the drop event is sent
let drag_enter = Closure::wrap(Box::new(|event: &web_sys::Event| {
event.prevent_default();
console_log!("Drag enter!");
}) as Box<dyn FnMut(&web_sys::Event)>);
let drag_over = Closure::wrap(Box::new(|event: &web_sys::Event| {
event.prevent_default();
console_log!("Drag over!");
}) as Box<dyn FnMut(&web_sys::Event)>);
// Register all the events on the window
web_sys::window()
.and_then(|win| {
win.set_ondragenter(Some(drag_enter.as_ref().unchecked_ref()));
win.set_ondragover(Some(drag_over.as_ref().unchecked_ref()));
win.set_ondrop(Some(drop_callback.as_ref().unchecked_ref()));
win.document()
})
.expect("Could not find window");
// Make sure our closures outlive this function
drag_enter.forget();
drag_over.forget();
drop_callback.forget();
}
Note that if you are using multiple threads, you may want something other than RefCell (maybe Mutex instead). Also, I also changed uses of JsCast::unchecked_from_js_ref(x) to the more canonical x.as_ref().unchecked_ref().

How to handle exception (Err) in unwrap_or_else?

struct OverflowError {}
fn test_unwrap() -> Result<String, OverflowError> {
let a: Result<String, u8> = Err(100);
let a: String = a.unwrap_or_else(|err| {
if err < 100 {
String::from("Ok")
} else {
// I want to return from the function (not just the closure)
// This is compile error with error:
// "the ? operator can only be used in a closure that returns Result or Option"
Err(OverflowError {})?
}
});
Ok(a)
}
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/lib.rs:13:13
|
6 | let a: String = a.unwrap_or_else(|err| {
| ______________________________________-
7 | | if err < 100 {
8 | | String::from("Ok")
9 | | } else {
... |
13 | | Err(OverflowError {})?
| | ^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a closure that returns `std::string::String`
14 | | }
15 | | });
| |_____- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `std::string::String`
= note: required by `std::ops::Try::from_error`
It's the simplified version of my code. Basically inside the unwrap_or_else closure, there might be a conditional error (e.g. IOError). In such cases, I'd like to terminate the function early (using ?). But obviously it doesn't work since it is currently in a closure and the closure doesn't expect a Result type.
What's the best practice to handle this?
What you want is or_else():
struct OverflowError {}
fn test_unwrap() -> Result<String, OverflowError> {
let a: Result<String, u8> = Err(100);
let a: String = a.or_else(|err| {
if err < 100 {
Ok(String::from("Ok"))
} else {
Err(OverflowError {})
}
})?;
Ok(a)
}
Simplified:
struct OverflowError {}
fn test_unwrap() -> Result<String, OverflowError> {
Err(100).or_else(|err| {
if err < 100 {
Ok(String::from("Ok"))
} else {
Err(OverflowError {})
}
})
}

Resources