I am starting to learn Rust with Yew. I followed the counter example in the documentation and now I am trying to implement an input functionality. I want to show the input value in html. I am able to console.log it but not to render it in the browser.
What am I missing?
This is the code I have so far:
use wasm_bindgen::{JsCast, UnwrapThrowExt};
use web_sys::HtmlInputElement;
use yew::prelude::*;
#[function_component]
fn App() -> Html {
let name = use_state(|| "");
let oninput = Callback::from(move |input_event: InputEvent| {
let name = name.clone();
let target: HtmlInputElement = input_event
.target()
.unwrap_throw()
.dyn_into()
.unwrap_throw();
//web_sys::console::log_1(&target.value().into()); // <- can console the value.
move |_: HtmlInputElement| name.set(&target.value().as_str());
});
html! {
<div>
<input {oninput} />
<p>{"name: "}<h5>{name}</h5></p> // <-- here is the error
</div>
}
}
fn main() {
yew::Renderer::<App>::new().render();
}
But I get this error:
error[E0277]: `UseStateHandle<&str>` doesn't implement `std::fmt::Display`
--> src/main.rs:22:29
|
22 | <p>{"name"}<h5>{name}</h5></p>
| ^^^^
| |
| `UseStateHandle<&str>` cannot be formatted with the default formatter
| required by a bound introduced by this call
|
= help: the trait `std::fmt::Display` is not implemented for `UseStateHandle<&str>`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required for `UseStateHandle<&str>` to implement `ToString`
= note: required for `VNode` to implement `From<UseStateHandle<&str>>`
= note: required for `UseStateHandle<&str>` to implement `Into<VNode>`
= note: 2 redundant requirements hidden
= note: required for `UseStateHandle<&str>` to implement `Into<NodeSeq<UseStateHandle<&str>, VNode>>`
I tried to use name.get(), &name, name.clone() and combination of those but I always get an error. Could you explain me why I cannot get the value to show in the browser?
I appreciate any help.
To use the value in the state, you need to dereference it:
<p>{"name: "}<h5>{*name}</h5></p>
However, now you will see another errors:
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:23:46
|
16 | let name = name.clone();
| ---- lifetime `'1` appears in the type of `name`
...
23 | move |_: HtmlInputElement| name.set(&target.value().as_str());
| ----------^^^^^^^^^^^^^^----------
| | | |
| | | temporary value is freed at the end of this statement
| | creates a temporary value which is freed while still in use
| argument requires that borrow lasts for `'1`
error[E0382]: borrow of moved value: `name`
--> src/main.rs:29:31
|
14 | let name = use_state(|| "");
| ---- move occurs because `name` has type `UseStateHandle<&str>`, which does not implement the `Copy` trait
15 | let oninput = Callback::from(move |input_event: InputEvent| {
| ------------------------------ value moved into closure here
16 | let name = name.clone();
| ---- variable moved due to use in closure
...
29 | <p>{"name: "}<h5>{*name}</h5></p> // <-- here is the error
| ^^^^^ value borrowed here after move
|
= note: borrow occurs due to deref coercion to `&str`
note: deref defined here
--> /home/explorer/.cargo/registry/src/github.com-1ecc6299db9ec823/yew-0.20.0/src/functional/hooks/use_state.rs:128:5
|
128 | type Target = T;
| ^^^^^^^^^^^
The first error is because you need to store String and not &str. The second is because you need to clone() the state before the closure:
#[function_component]
fn App() -> Html {
let name = use_state(|| String::new());
let oninput = Callback::from({
let name = name.clone();
move |input_event: InputEvent| {
let target: HtmlInputElement = input_event
.target()
.unwrap_throw()
.dyn_into()
.unwrap_throw();
//web_sys::console::log_1(&target.value().into()); // <- can console the value.
name.set(target.value());
}
});
html! {
<div>
<input {oninput} />
<p>{"name: "}<h5>{&*name}</h5></p>
</div>
}
}
Related
I am trying to use a channel to communicate between an event handler and the main thread of my program using async rust. The event handler in question is this one from the matrix-rust-sdk.
I can see that this exact pattern is used in the code here.
But when I tried literally the same thing in my own code, it gives me a really strange lifetime error.
error: lifetime may not live long enough
--> src/main.rs:84:13
|
80 | move |event: OriginalSyncRoomMessageEvent, room: Room| {
| ------------------------------------------------------
| | |
| | return type of closure `impl futures_util::Future<Output = ()>` contains a lifetime `'2`
| lifetime `'1` represents this closure's body
...
84 | / async move {
85 | | if let Room::Joined(room) = room {
86 | | if room.room_id() == room_id {
87 | | match event.content.msgtype {
... |
94 | | }
95 | | }
| |_____________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
I tried to make a much simpler example, and the weird lifetime error remains:
use tokio::sync::mpsc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let (tx, _rx) = mpsc::channel(32);
let closure = move || async {
at.send("hi");
};
Ok(())
}
Gives me:
error: lifetime may not live long enough
--> src/main.rs:9:27
|
9 | let closure = move || async {
| ___________________-------_^
| | | |
| | | return type of closure `impl Future<Output = ()>` contains a lifetime `'2`
| | lifetime `'1` represents this closure's body
10 | | at.send("hi");
11 | | };
| |_____^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
So how can I use a channel in an async closure? Why doesn't my code work when the code in the matrix-rust-sdk does?
I think you meant || async move instead of move || async.
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (tx, _rx) = mpsc::channel(32);
let closure = || async move {
tx.send("hi").await.unwrap();
};
closure().await;
}
I think in most cases, |args| async move {} is what you want to use if you want to create an async closure. But I don't completely understand the differences either.
For more infos, this might help:
What is the difference between `|_| async move {}` and `async move |_| {}`.
I don't think your minimal example represents your actual problem of your real code, though. This is a minimal example that represents the real problem:
#[derive(Clone, Debug)]
struct RoomId(u32);
#[derive(Clone, Debug)]
struct Room {
id: RoomId,
}
impl Room {
fn room_id(&self) -> &RoomId {
&self.id
}
}
#[tokio::main]
async fn main() {
let dm_room = Room { id: RoomId(42) };
let dm_room_closure = dm_room.clone();
let closure = move || {
let room_id = dm_room_closure.room_id();
async move {
println!("{}", room_id.0);
}
};
closure().await;
}
error: lifetime may not live long enough
--> src/main.rs:23:9
|
20 | let closure = move || {
| -------
| | |
| | return type of closure `impl Future<Output = ()>` contains a lifetime `'2`
| lifetime `'1` represents this closure's body
...
23 | / async move {
24 | | println!("{}", room_id.0);
25 | | }
| |_________^ returning this value requires that `'1` must outlive `'2`
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
The real problem here is caused by the fact that room_id contains a reference to dm_room_closure, but dm_room_closure does not get kept alive by the innermost async move context.
To fix this, make sure that the async move keeps dm_room_closure alive by moving it in as well. In this case, this is as simple as creating the room_id variable inside of the async move:
#[derive(Clone, Debug)]
struct RoomId(u32);
#[derive(Clone, Debug)]
struct Room {
id: RoomId,
}
impl Room {
fn room_id(&self) -> &RoomId {
&self.id
}
}
#[tokio::main]
async fn main() {
let dm_room = Room { id: RoomId(42) };
let dm_room_closure = dm_room.clone();
let closure = move || {
async move {
let room_id = dm_room_closure.room_id();
println!("{}", room_id.0);
}
};
closure().await;
}
42
So I finally fixed the error. It turns out that something in Room doesn't implement Copy and therefore it was causing some sort of state sharing despite the Clones. I fixed it by passing the RoomId as a string. Since the lifetime error message is entirely opaque, there was no way to see which moved variable was actually causing the problem. Off to file a compiler bug report.
This is part of my code, the error in rest move |event: Event|
pub struct FileStream{
el : HtmlInputElement,
}
impl FileStream {
pub fn new(el : HtmlInputElement) -> FileStream {
FileStream {el}
}
pub fn get_file(&self){
let file = self.el.files().unwrap().get(0).unwrap();
let file_reader = FileReader::new().unwrap();
file_reader.read_as_data_url(&file).unwrap();
let onloadend = Closure::wrap(Box::new(move |event: Event| {
let file_reader = file_reader.unchecked_into::<web_sys::FileReader>();
let data_url = file_reader.result().unwrap();
let data_url = data_url.unchecked_into::<JsValue>();
let data_url = data_url.as_string().unwrap();
let audio_el = self.el.unchecked_into::<HtmlAudioElement>();
audio_el.set_src(&data_url);
}) as Box<dyn FnMut(_)>);
file_reader.set_onloadend(Some(onloadend.as_ref().unchecked_ref()));
}
}
I don't know how to fix this error...
error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
--> src/lib.rs:29:48
|
29 | let onloadend = Closure::wrap(Box::new(move |event: Event| {
| - ^^^^^^^^^^^^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
| _______________________________________|
| |
30 | | let file_reader = file_reader.unchecked_into::<web_sys::FileReader>();
| | ----------- closure is `FnOnce` because it moves the variable `file_reader` out of its environment
31 | | let data_url = file_reader.result().unwrap();
32 | | let data_url = data_url.unchecked_into::<JsValue>();
... |
35 | | audio_el.set_src(&data_url);
36 | | }) as Box<dyn FnMut(_)>);
| |__________- the requirement to implement `FnMut` derives from here
For more information about this error, try `rustc --explain E0525`.
When the closure is created, it moves file_reader into itself (because it is declared as a move || closure). Then, when the closure is called, it consumes file_reader via calling unchecked_into(). Therefore, if the closure were to be called a second time, it would no longer have a file_reader to use. That's why the compiler decides the closure is only a “FnOnce”.
On the other hand, the set_onloadend callback mechanism wants a function that can be called more than once, in case the underlying event somehow happens more than once.
The general solution to this problem is to adjust your function so that it can be called more than once — as far as the compiler is concerned. You can do this by putting the value that must be moved in an Option outside the closure, then using Option::take() inside the closure to get it back out — thus leaving the Option value to be None if the function ever gets called again.
However, in this case, it would be simpler to just remove the entire unchecked_into() line. As far as I can see, there is no need for it, because the object is already typed as a FileReader. (But I haven't tried compiling your code to see if it works without, so maybe I missed something.)
I want to list all the available block devices in the system using a dbus implementation called zbus.
The UDisks2 documentation mentions a method call on org.freedesktop.UDisks2.Manager interface called GetBlockDevices which accepts IN a{sv} options, OUT ao block_objects for method parameters.
Using zbus, I write:
use std::error::Error;
use std::result::Result;
use zbus::{Connection, Proxy};
fn main() -> Result<(), Box<dyn Error>> {
let connection = Connection::new_system()?;
let p = Proxy::new(
&connection,
"org.freedesktop.UDisks2",
"/org/freedesktop/UDisks2/Manager",
"org.freedesktop.UDisks2.Manager",
)?;
let resp: Vec<zvariant::ObjectPath> = p.call("GetBlockDevices", &std::collections::HashMap::<String, zvariant::Value>::new())?;
dbg!(resp);
Ok(())
}
As far as I understand, zvariant Values represent a DBus Variant. However I get the following error:
error: implementation of `serde::de::Deserialize` is not general enough
--> src/main.rs:13:45
|
13 | let resp: Vec<zvariant::ObjectPath> = p.call("GetBlockDevices", &std::collections::HashMap::<String, zvariant::Value>::new())?;
| ^^^^ implementation of `serde::de::Deserialize` is not general enough
|
::: /home/adnan338/.cargo/registry/src/github.com-1ecc6299db9ec823/serde-1.0.115/src/de/mod.rs:531:1
|
531 | / pub trait Deserialize<'de>: Sized {
532 | | /// Deserialize this value from the given Serde deserializer.
533 | | ///
534 | | /// See the [Implementing `Deserialize`][impl-deserialize] section of the
... |
569 | | }
570 | | }
| |_- trait `serde::de::Deserialize` defined here
|
= note: `std::vec::Vec<zvariant::object_path::ObjectPath<'_>>` must implement `serde::de::Deserialize<'0>`, for any lifetime `'0`...
= note: ...but `std::vec::Vec<zvariant::object_path::ObjectPath<'_>>` actually implements `serde::de::Deserialize<'1>`, for some specific lifetime `'1`
What's causing this and how can I avoid this error?
Firstly, thanks for trying out our crate. The issue is that zbus::Proxy::call expects the return value to be an owned one, while you're deserializing to an unowned type. Both of the the following work:
// OwnedObjectPath require zvariant >= 2.2.0
let resp: Vec<zvariant::OwnedObjectPath> = p.call(
"GetBlockDevices",
&std::collections::HashMap::<String, zvariant::Value>::new(),
)?;
let resp = p.call_method(
"GetBlockDevices",
&std::collections::HashMap::<String, zvariant::Value>::new(),
)?;
let resp: Vec<zvariant::ObjectPath> = resp.body()?;
Due to the possibility of there being a large number of objects, I'd like to have a way to add them to the select list, remove them for processing and then add them back. All, without having to rebuild the select list each time an object is added back for waiting. It looks something like this:
use std::collections::HashMap;
use crossbeam::{Select, Sender, Receiver};
struct WaitList <'a> {
sel: Select<'a>,
objects: HashMap<u128, Object>,
sel_index: HashMap<usize, u128>,
}
impl<'a> WaitList<'a> {
fn new () -> Self { Self { sel: Select::new(), objects: HashMap::new(), sel_index: HashMap::new() } }
fn select(&self) -> &Object {
let oper = self.sel.select();
let index = oper.index();
let id = self.sel_index.get(&index).unwrap();
let obj = self.objects.get(&id).unwrap();
obj.cmd = oper.recv(&obj.receiver).unwrap();
self.sel.remove(index);
obj
}
fn add_object(&self, object: Object) {
let id = object.id;
self.objects.insert(id, object);
self.add_select(id);
}
fn add_select(&self, id: u128) {
let idx = self.sel.recv(&self.objects.get(&id).unwrap().receiver);
self.sel_index.insert(idx, id);
}
}
Over time the select list will contain more dead entries, then live, and I'll rebuild it at that time. But, I'd like to not have to rebuild it every time. Here's the detailed error message:
Checking test-select v0.1.0 (/Users/bruce/Projects/rust/examples/test-select)
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:28:47
|
28 | let idx = self.sel.recv(&self.objects.get(&id).unwrap().receiver);
| ^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 27:5...
--> src/main.rs:27:5
|
27 | / fn add_select(&self, id: u128) {
28 | | let idx = self.sel.recv(&self.objects.get(&id).unwrap().receiver);
29 | | self.sel_index.insert(idx, id);
30 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:28:34
|
28 | let idx = self.sel.recv(&self.objects.get(&id).unwrap().receiver);
| ^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 9:6...
--> src/main.rs:9:6
|
9 | impl<'a> WaitList<'a> {
| ^^
note: ...so that the types are compatible
--> src/main.rs:28:28
|
28 | let idx = self.sel.recv(&self.objects.get(&id).unwrap().receiver);
| ^^^^
= note: expected `&mut crossbeam::Select<'_>`
found `&mut crossbeam::Select<'a>`
While I believe I understand the issue, that the borrow of the receiver from the hash table doesn't live long enough, I'm having a difficult time trying to come up with an alternative -- and I'm not seeing a clean way to borrow the information. I considered creating a struct to contain the borrow, and using that instead of a plain id in the wait sel_index however that runs into the same lifetime problem.
struct SingleWaiter<'a> {
id: u128,
receiver: &'a Receiver::<Command>
}
I feel like I'm missing something or not understanding something, as it seems like it shouldn't be that difficult to do what I want to do. I can imagine that the choice of HashMap for holding object might be the issue, a Vec felt wrong, as I'm adding and inserting. BTW, the HashMap isn't normally part of the waitlist. It is part of something else, but the problem remains the same irregardless of where the HashMap lives.
When compiling the following code:
fn main() {
let mut fields = Vec::new();
let pusher = &mut |a: &str| {
fields.push(a);
};
}
The compiler gives me the following error:
error: borrowed data cannot be stored outside of its closure
--> src/main.rs:4:21
|
3 | let pusher = &mut |a: &str| {
| ------ --------- ...because it cannot outlive this closure
| |
| borrowed data cannot be stored into here...
4 | fields.push(a);
| ^ cannot be stored outside of its closure
And in later versions of Rust:
error[E0521]: borrowed data escapes outside of closure
--> src/main.rs:4:9
|
2 | let mut fields = Vec::new();
| ---------- `fields` declared here, outside of the closure body
3 | let pusher = &mut |a: &str| {
| - `a` is a reference that is only valid in the closure body
4 | fields.push(a);
| ^^^^^^^^^^^^^^ `a` escapes the closure body here
What does this error mean, and how can I fix my code?
It means exactly what it says: that the data you are borrowing only lives for the duration of the closure. Attempting to store it outside of the closure would expose the code to memory unsafety.
This arises because the inferred lifetime of the closure's argument has no relation to the lifetimes stored in the Vec.
Generally, this isn't a problem you experience because something has caused more type inference to happen. In this case, you can add a type to fields and remove it from the closure:
let mut fields: Vec<&str> = Vec::new();
let pusher = |a| fields.push(a);