Use paho-mqtt on Rust - rust

I have seen in Crates.io that there is a crate which already implements MQTT. But when trying to use the example provided in the repo I got so many errors and as part of the learning process, I am considering fixing that.
after adding
[dependencies]
paho-mqtt = "0.7.1"
I tried to build using this main.rs:
use std::process;
extern crate paho_mqtt as mqtt;
fn main() {
// Create a client & define connect options
let cli = mqtt::Client::new("tcp://localhost:1883").unwrap_or_else(|err| {
println!("Error creating the client: {:?}", err);
process::exit(1);
});
let conn_opts = mqtt::ConnectOptionsBuilder::new()
.keep_alive_interval(Duration::from_secs(20))
.clean_session(true)
.finalize();
// Connect and wait for it to complete or fail
if let Err(e) = cli.connect(conn_opts).wait() {
println!("Unable to connect:\n\t{:?}", e);
process::exit(1);
}
// Create a message and publish it
let msg = mqtt::Message::new("test", "Hello world!");
let tok = cli.publish(msg);
if let Err(e) = tok.wait() {
println!("Error sending message: {:?}", e);
}
// Disconnect from the broker
let tok = cli.disconnect();
tok.wait().unwrap();
}
I get the following errors:
error[E0433]: failed to resolve: use of undeclared type or module `Duration`
--> src/main.rs:13:30
|
13 | .keep_alive_interval(Duration::from_secs(20))
| ^^^^^^^^ use of undeclared type or module `Duration`
error[E0599]: no method named `wait` found for enum `std::result::Result<mqtt::ServerResponse, mqtt::MqttError>` in the current scope
--> src/main.rs:18:44
|
18 | if let Err(e) = cli.connect(conn_opts).wait() {
| ^^^^ method not found in `std::result::Result<mqtt::ServerResponse, mqtt::MqttError>`
error[E0061]: this function takes 3 arguments but 2 arguments were supplied
--> src/main.rs:24:15
|
24 | let msg = mqtt::Message::new("test", "Hello world!");
| ^^^^^^^^^^^^^^^^^^ ------ -------------- supplied 2 arguments
| |
| expected 3 arguments
error[E0599]: no method named `wait` found for enum `std::result::Result<(), mqtt::MqttError>` in the current scope
--> src/main.rs:27:25
|
27 | if let Err(e) = tok.wait() {
| ^^^^ method not found in `std::result::Result<(), mqtt::MqttError>`
error[E0061]: this function takes 1 argument but 0 arguments were supplied
--> src/main.rs:32:19
|
32 | let tok = cli.disconnect();
| ^^^^^^^^^^- supplied 0 arguments
| |
| expected 1 argument
error[E0599]: no method named `wait` found for enum `std::result::Result<(), mqtt::MqttError>` in the current scope
--> src/main.rs:33:9
|
33 | tok.wait().unwrap();
| ^^^^ method not found in `std::result::Result<(), mqtt::MqttError>`
error: aborting due to 6 previous errors
the error in line 13 I could solve by using std::time::Duration
the error in line 24 I could solve by adding the QoS
What are the other errors that I am missing?

Related

How to use a Future as yew::callback::Callback

I have following callback which I'm registering to onsubmit event.
use gloo::net::http::Request;
use yew::prelude::*;
let on_submit = Callback::from(async move |ev: FocusEvent| {
ev.prevent_default();
let res = Request::post("https://formsubmit.co/srineshnisala#gmail.com")
.send()
.await
.unwrap();
assert_eq!(res.status(), 200);
});
However, I get following error when I use async
--> src/pages/contact/contact.rs:26:36
|
26 | let on_submit = Callback::from(async move |ev: FocusEvent| {
| _____________________--------------_^
| | |
| | required by a bound introduced by this call
27 | | ev.prevent_default();
28 | |
29 | | let res = Request::post("https://formsubmit.co/srineshnisala#gmail.com")
... |
34 | | assert_eq!(res.status(), 200);
35 | | });
| |_____^ expected `()`, found opaque type
|
::: /home/s1n7ax/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/mod.rs:72:43
|
72 | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- the found opaque type
|
= note: expected unit type `()`
found opaque type `impl Future<Output = ()>`
= note: required for `yew::Callback<yew::FocusEvent>` to implement `From<[closure#src/pages/contact/contact.rs:26:36: 26:63]>`
Rust version:
rustc 1.66.0-nightly (01af5040f 2022-10-04)
package versions:
yew = "0.19.3"
yew-router = "0.16.0"
gloo = "0.7.0" # console log and stuff
hyper = "0.14.19" # http requests
How to use an async function as a callback in yew?
You can't create a Callback from an async closure but you can use wasm_bindgen_futures::spawn_local to run a Future on the current thread:
use gloo::net::http::Request;
use yew::prelude::*;
let on_submit = Callback::from(move |ev: FocusEvent| {
ev.prevent_default();
wasm_bindgen_futures::spawn_local(async move {
let res = Request::post("https://formsubmit.co/srineshnisala#gmail.com")
.send()
.await
.unwrap();
assert_eq!(res.status(), 200);
});
});
You can't.
Callback::from(impl Fn(In) -> Out) is the only way to construct a non-noop Callback.
You have to either use block_on to perform an async operation synchronously, or use callback only to register an action in an external async context, for example using a channel.

How to declare a variable in `macro_rules!`?

I'm creating a macro called throw_error. I expected this to compile yet, it fails:
// Util structs + types
...
// Util macros
#[macro_export]
macro_rules! throw_error {
() => {
RaptorexError {
message: String::new(),
line: line!(),
file: file!().to_owned(),
}
};
($($msg:tt),*) => {
let mut final_msg = String::new();
$(
final_msg.push_str(&format!("{} ", $msg));
)*
// remove trailing whitespace
final_msg.pop();
RaptorexError {
message: final_msg,
line: line!(),
file: file!(),
}
}
}
// Util functions
...
I get several errors from using the macro in my other code.
Erros:
error: macro expansion ignores token `final_msg` and any following
--> /Users/henryboisdequin/Desktop/raptorex/raptorex_util/src/lib.rs:30:13
|
30 | final_msg.push_str(&format!("{} ", $msg));
| ^^^^^^^^^
|
::: compiler/src/parser/parser.rs:44:29
|
44 | _ => return Err(throw_error!("Unexpected token:", current_token)),
| ------------------------------------------------- help: you might be missing a semicolon here: `;`
| |
| caused by the macro expansion here
|
= note: the usage of `throw_error!` is likely invalid in expression context
error[E0658]: `let` expressions in this position are experimental
--> compiler/src/parser/parser.rs:44:29
|
44 | _ => return Err(throw_error!("Unexpected token:", current_token)),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
= help: add `#![feature(let_chains)]` to the crate attributes to enable
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: `let` expressions are not supported here
--> compiler/src/parser/parser.rs:44:29
|
44 | _ => return Err(throw_error!("Unexpected token:", current_token)),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: only supported directly in conditions of `if`- and `while`-expressions
= note: as well as when nested within `&&` and parenthesis in those conditions
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
warning: unused imports: `DATA_TYPES`, `KEYWORDS`
--> compiler/src/parser/parser.rs:3:28
|
3 | lexer::tokens::{Token, DATA_TYPES, KEYWORDS},
| ^^^^^^^^^^ ^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error[E0308]: mismatched types
--> compiler/src/parser/parser.rs:44:29
|
44 | _ => return Err(throw_error!("Unexpected token:", current_token)),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `raptorex_util::RaptorexError`, found `bool`
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 4 previous errors; 1 warning emitted
What is the cause of these errors and how can I fix it?
You need another set of {}s so the macro creates a block containing the statements instead of the individual statements themselves:
#[macro_export]
macro_rules! throw_error {
() => {
RaptorexError {
message: String::new(),
line: line!(),
file: file!().to_owned(),
}
};
($($msg:tt),*) => {
{ // <------------------
let mut final_msg = String::new();
$(
final_msg.push_str(&format!("{} ", $msg));
)*
// remove trailing whitespace
final_msg.pop();
RaptorexError {
message: final_msg,
line: line!(),
file: file!(),
}
} // <-------------------
}
}
The {} in the (...) => {} is part of the macro syntax and isn't part of the generated code.

Write to websocket stream

I am connecting to a websocket server with tungstenite from this example and using write.send from there
let connect_addr = env::args()
.nth(1)
.unwrap_or_else(|| panic!("this program requires at least one argument"));
let url = url::Url::parse(&connect_addr).unwrap();
let (stdin_tx, stdin_rx) = futures_channel::mpsc::unbounded();
tokio::spawn(read_stdin(stdin_tx));
let (ws_stream, _) = connect_async(url).await.expect("Failed to connect");
println!("WebSocket handshake has been successfully completed");
let (write, read) = ws_stream.split();
## THIS DOESNT WORK ###
write.send(Message::Text(format!("{}", "HELLO!")))
.await
.expect("Failed to send message");
I can't get the write to work. I get:
error[E0599]: no method named `send` found for struct `futures_util::stream::stream::split::SplitSink<tokio_tungstenite::WebSocketStream<tokio_tungstenite::stream::Stream<tokio::net::tcp::stream::TcpStream, tokio_native_tls::TlsStream<tokio::net::tcp::stream::TcpStream>>>, tungstenite::protocol::message::Message>` in the current scope
--> src/main.rs:28:15
|
28 | write.send(Message::Text(format!("{}", 8)))
| ^^^^ method not found in `futures_util::stream::stream::split::SplitSink<tokio_tungstenite::WebSocketStream<tokio_tungstenite::stream::Stream<tokio::net::tcp::stream::TcpStream, tokio_native_tls::TlsStream<tokio::net::tcp::stream::TcpStream>>>, tungstenite::protocol::message::Message>`
|
::: ../futures-util-0.3.5/src/sink/mod.rs:207:8
|
207 | fn send(&mut self, item: Item) -> Send<'_, Self, Item>
| ----
| |
| the method is available for `std::boxed::Box<futures_util::stream::stream::split::SplitSink<tokio_tungstenite::WebSocketStream<tokio_tungstenite::stream::Stream<tokio::net::tcp::stream::TcpStream, tokio_native_tls::TlsStream<tokio::net::tcp::stream::TcpStream>>>, tungstenite::protocol::message::Message>>` here
| the method is available for `std::sync::Arc<futures_util::stream::stream::split::SplitSink<tokio_tungstenite::WebSocketStream<tokio_tungstenite::stream::Stream<tokio::net::tcp::stream::TcpStream, tokio_native_tls::TlsStream<tokio::net::tcp::stream::TcpStream>>>, tungstenite::protocol::message::Message>>` here
| the method is available for `std::rc::Rc<futures_util::stream::stream::split::SplitSink<tokio_tungstenite::WebSocketStream<tokio_tungstenite::stream::Stream<tokio::net::tcp::stream::TcpStream, tokio_native_tls::TlsStream<tokio::net::tcp::stream::TcpStream>>>, tungstenite::protocol::message::Message>>` here

Borrowed and captured variable in RUST

I am working with paho_mqtt, and I want to send messages on topics according to those received. I divided my code into nested functions, and I use the variable cli (cf code below). I think I need to use references to have a working code, but I still don't figure how. Thank you for your help.
Code:
fn publish_fail_msg(cli: mqtt::AsyncClient, topic: String){
let msg = mqtt::Message::new(topic, "Fail", QOS[0]);
cli.publish(msg);
}
fn manage_msgs(cli: mqtt::AsyncClient, msg: mqtt::Message){
let topic = msg.topic();
let payload_str = msg.payload_str();
match topic {
"main" => publish_new_topic(cli, payload_str.to_string()),
_ => publish_fail_msg(cli, topic.to_string()),
}
}
fn main() -> mqtt::AsyncClient {
// Create the client connection
let mut cli = mqtt::AsyncClient::new(create_opts).unwrap_or_else(|e| {
println!("Error creating the client: {:?}", e);
process::exit(1);
});
// Attach a closure to the client to receive callback on incoming messages.
cli.set_message_callback(|_cli, msg| {
if let Some(msg) = msg {
manage_msgs(cli, msg);
}
});
cli
}
Error:
error[E0507]: cannot move out of captured variable in an `FnMut` closure
--> src/main.rs:230:25
|
212 | let mut cli = mqtt::AsyncClient::new(create_opts).unwrap_or_else(|e| {
| ------- captured outer variable
...
230 | manage_msgs(cli, msg);
| ^^^ cannot move out of captured variable in an `FnMut` closure
error[E0505]: cannot move out of `cli` because it is borrowed
--> src/main.rs:228:30
|
228 | cli.set_message_callback(|_cli, msg| {
| --- -------------------- ^^^^^^^^^^^ move out of `cli` occurs here
| | |
| | borrow later used by call
| borrow of `cli` occurs here
229 | if let Some(msg) = msg {
230 | manage_msgs(cli, msg);
| --- move occurs due to use in closure
error[E0382]: borrow of moved value: `cli`
--> src/main.rs:248:5
|
212 | let mut cli = mqtt::AsyncClient::new(create_opts).unwrap_or_else(|e| {
| ------- move occurs because `cli` has type `mqtt::AsyncClient`, which does not implement the `Copy` trait
...
228 | cli.set_message_callback(|_cli, msg| {
| ----------- value moved into closure here
229 | if let Some(msg) = msg {
230 | manage_msgs(cli, msg);
| --- variable moved due to use in closure
...
248 | cli.connect_with_callbacks(conn_opts, on_connect_success, on_connect_failure);
| ^^^ value borrowed here after move
The devil is in the details: I found the issue, which is the _cli. I replaced it with cli: &mqtt::AsyncClient and I could send the reference to my nested functions. Maybe there are better solutions for this, and it would be a pleasure to see them.

"Closure takes 0 arguments" on a closure that takes an argument

I am attempting to pass a closure to a function after a condition:
extern crate futures;
extern crate tokio_core;
use futures::prelude::*;
use futures::future;
use futures::unsync::*;
use tokio_core::reactor::*;
fn main() {
let mut core = Core::new().unwrap();
let h2 = core.handle();
let fun = |should: Result<bool, ()>| {
if should.unwrap_or(true) {
// do something
}
future::ok(())
};
let (tx, rx) = oneshot::channel::<bool>();
let dummy = true;
if dummy {
h2.spawn(rx.then(fun));
} else {
h2.spawn(future::ok(true).then(fun));
}
core.run(future::ok::<(), ()>(())).unwrap();
}
Here is the code in the playground, but it is getting a different error from the error baffling me. My error looks like:
error[E0593]: closure takes 0 arguments but 1 argument is required
--> src/client/forger.rs:123:25
|
57 | let fun = |should: Result<bool, ()>| {
| ___________________-
58 | | if !should.unwrap_or(false) {
59 | | return Box::new(future::ok::<(), ()>(()));
60 | | }
... |
117 | | Box::new(future::ok(()))
118 | | };
| |_________- takes 0 arguments
...
123 | h2.spawn(rx.then(fun));
| ^^^^ expected closure that takes 1 argument
Why does Rust say the function fun takes 0 arguments when it appears to quote it as taking one?
My Rust version is 1.22.1.
Turns out that my Rust version was 1.22.1, but the latest version is 1.23.0. This explains why the playground was returning a more accurate error: expected signature of fn(std::result::Result<bool, futures::Canceled>) -> _... which was the problem exactly.

Resources