Using warp.rs 0.2.2, let's consider a basic web service with one route for GET /:
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let getRoot = warp::get().and(warp::path::end()).and_then(routes::getRoot);
warp::serve(getRoot).run(([0, 0, 0, 0], 3030)).await;
Ok(())
}
My goal is to use ? for error handling in the route handlers, so let's write one that can error and return early in crate::routes:
use crate::errors::ServiceError;
use url::Url;
pub async fn getRoot() -> Result<impl warp::Reply, warp::Rejection> {
let _parsed_url = Url::parse(&"https://whydoesn.it/work?").map_err(ServiceError::from)?;
Ok("Hello world !")
}
This version works.
Here the error that's returned by Url::parse() is a url::ParseError
To convert between error types, from url::ParseError to ServiceError, then from ServiceError to warp::Rejection, I've written some error helpers in crate::errors:
#[derive(thiserror::Error, Debug)]
pub enum ServiceError {
#[error(transparent)]
Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error
}
impl warp::reject::Reject for ServiceError {}
impl From<ServiceError> for warp::reject::Rejection {
fn from(e: ServiceError) -> Self {
warp::reject::custom(e)
}
}
impl From<url::ParseError> for ServiceError {
fn from(e: url::ParseError) -> Self {
ServiceError::Other(e.into())
}
}
Now, the above works, and I'm trying to shorten the second code block to use ? for error handling directly, and convert automatically from the underlying error (here url::ParseError) to a warp::Rejection.
Here's what I've tried:
use crate::errors::ServiceError;
use url::Url;
pub async fn getRoot() -> Result<impl warp::Reply, ServiceError> {
let _parsed_url = Url::parse(&"https://whydoesn.it/work?")?;
Ok("Hello world !")
}
The url::ParseError returned by Url::Parse will convert fine into a ServiceError to return, but returning a ServiceError from my handler doesn't work.
The first compilation error I get is:
error[E0277]: the trait bound `errors::ServiceError: warp::reject::sealed::CombineRejection<warp::reject::Rejection>` is not satisfied
--> src/main.rs:102:54
|
102 | let getRoot = warp::get().and(warp::path::end()).and_then(routes::getRoot);
| ^^^^^^^^ the trait `warp::reject::sealed::CombineRejection<warp::reject::Rejection>` is not implemented for `errors::ServiceError`
Is there a way I can keep the short error handling using ? only and either:
make ServiceError implement warp::reject::sealed::CombineRejection<warp::reject::Rejection> ?
work around that ?
You can implement From to convert your error type into warp::Rejection using reject::custom. Rejection encapsulates custom types which you can later choose to inspect inside of a recover handler.
This example uses a plain error struct, but if you have an error enum you can match on the variants inside the recovery handler and perform different logic as needed.
use serde::Deserialize;
use snafu::{ensure, Snafu};
use std::convert::Infallible;
use warp::{
filters::{any, query, BoxedFilter},
http::StatusCode,
reject::Reject,
Filter, Rejection, Reply,
};
// A normal error type, created by SNAFU
#[derive(Debug, Snafu)]
#[snafu(display("Expected a value less than 10, but it was {}", value))]
struct LessThanTenError {
value: i32,
}
// A function that might fail
fn validate(value: i32) -> Result<i32, LessThanTenError> {
ensure!(value < 10, LessThanTenContext { value });
Ok(value)
}
// We need a custom type to later extract from the `Rejection`. In
// this case, we can reuse the error type itself.
impl Reject for LessThanTenError {}
// To allow using `?`, we implement a conversion from our error to
// `Rejection`
impl From<LessThanTenError> for Rejection {
fn from(other: LessThanTenError) -> Self {
warp::reject::custom(other)
}
}
#[tokio::main]
async fn main() {
let api = simple_math().recover(report_invalid);
let p: std::net::SocketAddr = "0.0.0.0:8888".parse().unwrap();
warp::serve(api).run(p).await;
}
#[derive(Debug, Deserialize)]
struct QueryParams {
a: i32,
b: i32,
}
fn simple_math() -> BoxedFilter<(impl Reply,)> {
any::any()
.and(query::query())
.and_then(|args: QueryParams| async move {
// Look at us using those question marks!
let a = validate(args.a)?;
let b = validate(args.b)?;
let sum = validate(a + b)?;
// We specify that we are returning an error type of
// `Rejection`, which allows the compiler to know what
// type to convert to when using `?` here.
Ok::<_, Rejection>(format!("The sum is {}", sum))
})
.boxed()
}
async fn report_invalid(r: Rejection) -> Result<impl Reply, Infallible> {
if let Some(e) = r.find::<LessThanTenError>() {
// It was our specific error type, do whatever we want. We
// will just print out the error text.
Ok(warp::reply::with_status(
e.to_string(),
StatusCode::BAD_REQUEST,
))
} else {
// Do prettier error reporting for the default error here.
Ok(warp::reply::with_status(
String::from("Something bad happened"),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
[dependencies]
serde = { version = "1.0.118", features = ["derive"] }
snafu = "0.6.10"
tokio = { version = "0.2.23", features = ["full"] }
warp = "0.2.5"
% curl 'http://127.0.0.1:8888'
< HTTP/1.1 500 Internal Server Error
Something bad happened
% curl -v 'http://127.0.0.1:8888?a=1&b=2'
< HTTP/1.1 200 OK
The sum is 3
% curl -v 'http://127.0.0.1:8888?a=6&b=5'
< HTTP/1.1 400 Bad Request
Expected a value less than 10, but it was 11
See also:
Is there a way to do validation as part of a filter in Warp?
When should I implement std::convert::From vs std::convert::Into?
How do you define custom `Error` types in Rust?
From my findings, there are two solutions.
Abandon ? in favor of your own macro that constructs and returns a response if there is an error.
Use PR #458 by cjbassi instead of the mainline release by:
Implementing warp::reply::Reply on your error type so that it converts into the correct user facing error message.
Replace warp = "0.2" with warp = { git = "https://github.com/cjbassi/warp.git", branch = "error"} in your Cargo.toml file
use .map_async instead of .and_then for handlers
Related
Is it possible to handle multiple different errors at once instead of individually in Rust without using additional functions? In short: what is the Rust equivalent to a try-catch statement?
A feature like this (First-class error handling with ? and catch) was suggested back in 2016, but I can't tell what came out of it and how a 2019 solution for such a problem might look like.
For example, doing something like this:
try {
do_step_1()?;
do_step_2()?;
do_step_3()?;
// etc
} catch {
alert_user("Failed to perform necessary steps");
}
Instead of:
match do_steps() {
Ok(_) => (),
_ => alert_user("Failed to perform necessary steps")
}
// Additional function:
fn do_steps() -> Result<(), Error>{
do_step_1()?;
do_step_2()?;
do_step_3()?;
// etc
Ok(())
}
My program has a function which checks a variety of different places in the registry for different data values and returns some aggregate data. It would need to use many of these try-cache statements with try-catch inside of other try-catch inside of loops.
There is no try catch statement in Rust. The closest approach is the ? operator.
However, you do not have to create a function and a match statement to resolve it in the end. You can define a closure in your scope and use ? operator inside the closure. Then throws are held in the closure return value and you can catch this wherever you want like following:
fn main() {
let do_steps = || -> Result<(), MyError> {
do_step_1()?;
do_step_2()?;
do_step_3()?;
Ok(())
};
if let Err(_err) = do_steps() {
println!("Failed to perform necessary steps");
}
}
Playground
Is it possible to handle multiple different errors at once instead of individually in Rust without using additional functions?
There is a anyhow crate for the error management in Rust mostly recommended nowadays.
As an alternative, There is a failure crate for the error management in Rust. Using Failure, you can chain, convert, concatenate the errors. After converting the error types to one common type, you can catch (handle) it easily.
Results in Rust can be chained using and_then. So you can do this:
if let Err(e) = do_step_1().and_then(do_step_2).and_then(do_step_3) {
println!("Failed to perform necessary steps");
}
or if you want a more compact syntax, you can do it with a macro:
macro_rules! attempt { // `try` is a reserved keyword
(#recurse ($a:expr) { } catch ($e:ident) $b:block) => {
if let Err ($e) = $a $b
};
(#recurse ($a:expr) { $e:expr; $($tail:tt)* } $($handler:tt)*) => {
attempt!{#recurse ($a.and_then (|_| $e)) { $($tail)* } $($handler)*}
};
({ $e:expr; $($tail:tt)* } $($handler:tt)*) => {
attempt!{#recurse ($e) { $($tail)* } $($handler)* }
};
}
attempt!{{
do_step1();
do_step2();
do_step3();
} catch (e) {
println!("Failed to perform necessary steps: {}", e);
}}
playground
There's also an unstable feature called try_blocks (https://doc.rust-lang.org/beta/unstable-book/language-features/try-blocks.html, https://github.com/rust-lang/rust/issues/31436)
Usage example:
#![feature(try_blocks)]
fn main() {
// you need to define the result type explicitly
let result: Result<(), Error> = try {
do_step_1()?;
do_step_2()?;
do_step_3()?;
};
if let Err(e) = result {
println!("Failed to perform necessary steps, ({:?})", e);
}
}
fn do_step_1() -> Result<(), Error> { Ok(()) }
fn do_step_2() -> Result<(), Error> { Ok(()) }
fn do_step_3() -> Result<(), Error> { Err(Error::SomeError) }
#[derive(Debug)]
enum Error {
SomeError,
}
I think match expression is equivalent of try/catch
match get_weather(location) {
Ok(report) => {
display_weather(location, &report);
}
Err(err) => {
println!("error querying the weather: {}", err);
// or write a better logic
}
}
We try to get weather report from api, if our request fails, handle the error other wise shows the result.
Not sure it's considered idiomatic Rust, but you can use an anonymous closure to achieve syntax similar to try/catch:
fn do_step_1() -> Result<(), String> { Ok(()) }
fn do_step_2() -> Result<(), String> { Err("error at step 2".to_string()) }
fn do_step_3() -> Result<(), String> { Ok(()) }
fn alert_user(s: &str) { println!("{}", s); }
fn main() {
(|| {
do_step_1()?;
do_step_2()?;
do_step_3()?;
Ok(())
})().unwrap_or_else(|_err: String| {
alert_user("Failed to perform the necessary steps");
})
}
The try and except concept is used in extremely vague terms. Since Rust is a strongly typed language, the user must write their own methods for handling errors by relying on the provided Option<T> and Result<T, E> enumerations or by defining their own accustomed enumerations.
See here for a more in-depth read for error-handling using enumerations.
The try macro is deprecated and has been replaced with the ? operator which makes it easier to organize and clean up error-handling because it can get messy. A main use for the ? operator would be that it allows you to implement the From trait for Result<T, E>'s Err(E) variant.
Here's a basic example:
use std::num::ParseIntError;
// Custom error-based enum with a single example
#[derive(Debug)]
enum Error {
ParseIntError(ParseIntError),
// Other errors...
}
// Then implement the `From` trait for each error so that the `?` operator knows what to do for each specified error.
impl From<ParseIntError> for Error {
fn from(error: ParseIntError) -> Self {
Self::ParseIntError(error)
}
}
// When using the `?` try operator, if the `Result` is an `Err` then it will basically act as `return Err(E)` returning that error value out to the current scope. If it is `Ok(T)`, it will simply unwrap the variant.
fn main() -> Result<(), Error> {
// This will return the value `69` as a `u8` type
let parsed_value_1 = "69".parse::<u8>()?;
println!("{}", parsed_value_1);
// Since parsing fails here, a `ParseIntError` will be returned to the current function. *Since the scope is the `main` function, it will automatically print the error after panicking.
let parsed_value_2 = "poop".parse::<u8>()?;
// Unreachable code
println!("{}", parsed_value_2);
Ok(())
}
I want to move a value into a tuple-type enum variant and obtain a reference to the value after it has been moved. I see how this is possible with an if let statement, but this seems like this should be unnecessary when the particular variant is known statically.
Is there any way to get the reference to the moved value without requiring an if let or match?
This code block is a simple illustration of my question (see below for a more challenging case):
enum Transport {
Car(u32), // horsepower
Horse(String), // name
}
fn do_something(x: &String) {
println!(x);
}
fn main() {
// Can I avoid needing this if, which is clearly redundant?
if let Transport::Horse(ref name) = Transport::Horse("daisy".into()) {
do_something(name);
}
else {
// Can never happen
}
// I tried the following, it gives:
// "error[E0005]: refutable pattern in local binding: `Car(_)` not covered"
let Transport::Horse(ref name) = Transport::Horse("daisy".into());
}
It is easy to find ways to side-step the issue in the above code, since there are no real interface requirements. Consider instead the following example, where I am building a simple API for building trees (where each node can have n children). Nodes have an add_child_node method returning a reference to the node that was added, to allow chaining of calls to quickly build deep trees. (It is debatable whether this is a good API, but that is irrelevant to the question). add_child_node must return a mutable reference to the contents of an enum variant. Is the if let required in this example (without changing the API)?
struct Node {
children: Vec<Child>,
// ...
}
enum Child {
Node(Node),
Leaf
}
impl Node {
fn add_child_node(&mut self, node: Node) -> &mut Node {
self.children.push(Child::Node(node));
// It seems like this if should be unnecessary
if let Some(&mut Child::Node(ref mut x)) = self.children.last() {
return x;
}
// Required to compile, since we must return something
unreachable!();
}
fn add_child_leaf(&mut self) {
// ...
}
}
No. You can use unreachable!() for the else case, and it's usually clear even without message/comment what's going on. The compiler is also very likely to optimize the check away.
If the variants have the same type you can implement AsRef and use the Transport as a &str:
enum Transport {
Car(String),
Horse(String),
}
fn do_something<S: AsRef<str>>(x: &S) {
println!("{}", x.as_ref());
}
impl AsRef<str> for Transport {
fn as_ref(&self) -> &str {
match self {
Transport::Car(s) => s,
Transport::Horse(s) => s,
}
}
}
fn main() {
let transport = Transport::Horse("daisy".into());
do_something(&transport)
}
Playground
Otherwise you need to use a let if binding as you are doing. No need to use an else clause if you don't want to:
if let Transport::Horse(ref name) = Transport::Horse("daisy".into()) {
do_something(name);
}
define From<Transport> for String:
…
impl From<Transport> for String {
fn from(t: Transport) -> String {
match t {
Transport::Car(value) => value.to_string(),
Transport::Horse(name) => name,
}
}
}
fn do_something(x: Transport) {
println!("{}", String::from(x));
}
fn main() {
let horse = Transport::Horse("daisy".to_string());
let car = Transport::Car(150);
do_something(horse);
do_something(car);
}
This is what I have, but I want to avoid using unwrap on my reqwest values:
extern crate base64;
extern crate reqwest;
use serde_json;
use serde_json::json;
pub fn perform_get(id: String) -> serde_json::value::Value {
let client = reqwest::Client::builder().build().unwrap();
let url = String::from("SomeURL");
let res = client.get(&url).send().unwrap().text();
let mut v = json!(null);
match res {
Ok(n) => {
v = serde_json::from_str(&n).unwrap();
}
Err(r) => {
println!("Something wrong happened {:?}", r);
}
}
v
}
fn main() {
println!("Hi there! i want the function above to return a result instead of a Serde value so I can handle the error in main!");
}
Here is a link to a rust playground example
The official Rust book, The Rust Programming Language, is freely available online. It has an entire chapter on using Result, explaining introductory topics such as the Result enum and how to use it.
How to return a Result containing a serde_json::Value?
The same way you return a Result of any type; there's nothing special about Value:
use serde_json::json; // 1.0.38
pub fn ok_example() -> Result<serde_json::value::Value, i32> {
Ok(json! { "success" })
}
pub fn err_example() -> Result<serde_json::value::Value, i32> {
Err(42)
}
If you have a function that returns a Result, you can use the question mark operator (?) to exit early from a function on error, returning the error. This is a concise way to avoid unwrap or expect:
fn use_them() -> Result<(), i32> {
let ok = ok_example()?;
println!("{:?}", ok);
let err = err_example()?;
println!("{:?}", err); // Never executed, we always exit due to the `?`
Ok(()) // Never executed
}
This is just a basic example.
Applied to your MCVE, it would look something like:
use reqwest; // 0.9.10
use serde_json::Value; // 1.0.38
type Error = Box<dyn std::error::Error>;
pub fn perform_get(_id: String) -> Result<Value, Error> {
let client = reqwest::Client::builder().build()?;
let url = String::from("SomeURL");
let res = client.get(&url).send()?.text()?;
let v = serde_json::from_str(&res)?;
Ok(v)
}
Here, I'm using the trait object Box<dyn std::error::Error> to handle any kind of error (great for quick programs and examples). I then sprinkle ? on every method that could fail (i.e. returns a Result) and end the function with an explicit Ok for the final value.
Note that the panic and the never-used null value can be removed with this style.
See also:
What is this question mark operator about?
Rust proper error handling (auto convert from one error type to another with question mark)
Rust return result error from fn
Return value from match to Err(e)
What is the idiomatic way to handle/unwrap nested Result types?
better practice to return a Result
See also:
Should I avoid unwrap in production application?
If you are in the user side I would suggest to use Box<dyn std::error::Error>, this allow to return every type that implement Error, ? will convert the concrete error type to the dynamic boxed trait, this add a little overhead when there is an error but when error are not expected or really rare this is not a big deal.
use reqwest;
use serde_json::value::Value;
use std::error::Error;
fn perform_get(_id: String) -> Result<Value, Box<dyn Error>> {
let client = reqwest::Client::builder().build()?;
let url = String::from("SomeURL");
let res = client.get(&url).send()?.text()?;
let v = serde_json::from_str(&res)?;
Ok(v)
// last two line could be serde_json::from_str(&res).map_err(std::convert::Into::into)
}
fn main() {
println!("{:?}", perform_get("hello".to_string()));
}
This produce the following error:
Err(Error { kind: Url(RelativeUrlWithoutBase), url: None })
The kind smart folks over at Rust Discord helped me solve this one. (user noc)
extern crate base64;
extern crate reqwest;
pub fn get_jira_ticket() -> Result<serde_json::value::Value, reqwest::Error> {
let client = reqwest::Client::builder().build().unwrap();
let url = String::from("SomeURL");
let res = client.get(&url).send().and_then(|mut r| r.json());
res
}
fn main() {
println!("This works");
}
The key part was this in the header for the return
-> Result<serde_json::value::Value, reqwest::Error>
And this here to actually return the data.
client.get(&url).send().and_then(|mut r| r.json());
I'm building a library in Rust that has a send method that performs HTTP requests against a local RPC server using reqwest.
This method returns a generic type R in a Result where R: DeserializeOwned. After making the correct types for every response, serde_json::from_str() can get me the type.
If there is no response upon a request, how can I make send still return something meaningful?
This is the code I have now:
fn send<R, T>(
&self,
request: &RpcRequest<T>,
) -> Result<R, ApiError>
where
T: Serialize + Debug,
R: DeserializeOwned + Debug,
let res = serde_json::from_str(&buf).map_err(|err| ClientError::Json(err))
I am now forced to create and return an Err, but technically, the request returning no response is expected behavior, so I want to return something other than an Err.
I tried to work around this by wrapping R with Option, but that means I have to double unwrap every response, and 98% of the responses from reqwest do have data in their response, so it feels a bit like overkill.
I also tried to return a self-made EmptyResponse type, but the compiler complains: expected type R, found type EmptyResponse. I think returning a type EmptyResponse would be what I want, but maybe someone can shed some tips on how to maybe do this even better.
You can return an Result<Option<R>, ApiError> as shown in the documentation, then match it like this:
match sender.send(request) {
Ok(Some(r)) => {
// process response
}
Ok(None) => {
// process empty response
}
Err(e) => {
// process error
}
}
// or
if let Ok(Some(r)) = sender.send(request) {
// process response
}
I tried to work around this by wrapping R with Option, but that means I have to double unwrap every response, and 98% of the responses from reqwest do have data in their response, so it feels a bit like overkill.
Unwrapping the Option is a very cheap operation, there's nothing to be worried about.
The pragmatic answer is to have two functions:
fn send<R, T>(&self, request: &RpcRequest<T>) -> Result<R, ApiError>
where
T: Serialize + Debug,
R: DeserializeOwned + Debug,
fn send_no_response<T>(&self, request: &RpcRequest<T>) -> Result<(), ApiError>
where
T: Serialize + Debug,
If your server happens to return a value that can be deserialized into the type (), then you can avoid the overhead of two functions. However, this is not the case for JSON, one of the most common formats:
use serde::de::DeserializeOwned; // 1.0.85
use serde_json; // 1.0.37
type Error = Box<std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
fn send<R>() -> Result<R, Error>
where
R: DeserializeOwned,
{
serde_json::from_str("").map_err(Into::into)
}
fn main() {
let _r: () = send().expect("Unable to deserialize");
}
This panics:
Unable to deserialize: Error("EOF while parsing a value", line: 1, column: 0)
In a world with specialization, you can use it and a helper trait to reduce back to one function:
#![feature(specialization)]
use serde::de::DeserializeOwned; // 1.0.85
use serde_json; // 1.0.37
type Error = Box<std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;
type ApiResponse = &'static str;
trait FromApi: Sized {
fn convert(response: ApiResponse) -> Result<Self, Error>;
}
impl<R> FromApi for R
where
R: DeserializeOwned,
{
default fn convert(response: ApiResponse) -> Result<R, Error> {
eprintln!("deserializing the response");
serde_json::from_str(response).map_err(Into::into)
}
}
impl FromApi for () {
fn convert(_response: ApiResponse) -> Result<Self, Error> {
eprintln!("Ignoring the response");
Ok(())
}
}
fn send<R: FromApi>() -> Result<R> {
eprintln!(r#""sending" the request"#);
let api_response = "";
R::convert(api_response)
}
fn main() {
let _r: () = send().expect("Unable to deserialize");
}
I am writing a simple websocket server named BoltServer based on Rust websocket crate(Code is incomplete, I just started). I am using the example program as the base. However the example programs are not modular (having very long methods). So I am trying to break them up into structs and methods. I want to spawn two threads for each client. One sends messages and the other one receives messages. So here, I want to capture all variables used by the thread in a struct and then call the run method in the impl.
extern crate websocket;
extern crate time;
extern crate rustc_serialize;
pub mod ws {
use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::net::ToSocketAddrs;
use websocket;
use websocket::{Server, Message, Sender, Receiver};
use websocket::server::Connection;
use websocket::stream::WebSocketStream;
use std::str::from_utf8;
struct BoltUser {
user_id: u32,
my_tx: mpsc::Sender<String>,
}
struct Broadcaster {
my_rx: mpsc::Receiver<String>,
}
impl Broadcaster {
fn new(receiver: mpsc::Receiver<String>) -> Broadcaster {
Broadcaster { my_rx: receiver }
}
fn run(self) {
while let Ok(msg) = self.my_rx.recv() {
println!("Broadcaster got message: {}", msg);
}
}
}
struct SocketReader {}
impl SocketReader {
fn run(self) {}
}
struct SocketWriter {
my_rx: mpsc::Receiver<String>,
sender: Sender,
}
impl SocketWriter {
fn run(self) {
while let Ok(message) = self.my_rx.recv() {
}
}
}
pub struct BoltServer {
address: String,
connected_users: Arc<Mutex<Vec<BoltUser>>>,
}
impl BoltServer {
pub fn new(address: &str) -> BoltServer {
BoltServer {
address: address.to_string(),
connected_users: Arc::new(Mutex::new(vec![])),
}
}
fn handshake(&mut self,
connection: Connection<WebSocketStream, WebSocketStream>)
-> (SocketWriter, SocketReader) {
let request = connection.read_request().unwrap();
// println!("thread-> Accepting request...");
let response = request.accept();
let (mut sender, mut receiver) = response.send().unwrap().split();
let (user_tx, user_rx) = mpsc::channel::<String>();//Create a channel for writer
let socket_writer = SocketWriter {
my_rx: user_rx,
sender: sender,
};
let socket_reader = SocketReader {};
(socket_writer, socket_reader)
}
pub fn start(&mut self) {
println!("Starting");
let (broadcaster_tx, broadcaster_rx) = mpsc::channel::<String>();
let broadcaster = Broadcaster::new(broadcaster_rx);
let handle = thread::Builder::new()
.name("Broadcaster".to_string())
.spawn(move || broadcaster.run());
let server = Server::bind(&*self.address).unwrap();
let mut user_id: u32 = 0;
// Block and process connection request from a new client
for connection in server {
user_id = user_id + 1;//Create a new user id
let (socket_writer, socket_reader) = self.handshake(connection);
thread::Builder::new()
.name("Socket writer".to_string())
.spawn(move || socket_writer.run());
thread::Builder::new()
.name("Socket reader".to_string())
.spawn(move || socket_reader.run());
}
handle.unwrap().join();
println!("Finished");
}
}
}
The following code gives an idea of what I want to achieve.
// Block and process connection request from a new client
for connection in server {
user_id = user_id + 1;//Create a new user id
let (socket_writer, socket_reader) = self.handshake(connection);
thread::Builder::new().name("Socket writer".to_string()).spawn(move || {
socket_writer.run()
});
thread::Builder::new().name("Socket reader".to_string()).spawn(move || {
socket_reader.run()
});
}
Here I am stuck in the handshake method. I am not able to initialize the SocketWriter struct with the sender that I am getting by calling the split method in the library. I am getting the following compilation error:
error[E0038]: the trait `websocket::Sender` cannot be made into an object
--> src/lib.rs:46:9
|
46 | sender:Sender,
| ^^^^^^^^^^^^^ the trait `websocket::Sender` cannot be made into an object
|
= note: method `send_dataframe` has generic type parameters
= note: method `send_message` has generic type parameters
The error is telling you the immediate problem:
46 | sender:Sender,
| ^^^^^^^^^^^^^ the trait `websocket::Sender` cannot be made into an object
First of all, a variable/field can't have a plain trait type (but &Trait can be possible), but also the websocket::Sender trait is not object safe; it has generic methods which can't work dynamically (ie vtable methods have to have a fixed type).
Instead, you have to have a concrete type (you could also make it a generic struct).
It's not obvious what the right type is, so I like to get the compiler to tell me. So first try the simplest possible:
sender: (),
The compiler replies with some information:
| ^^^^^^ expected (), found struct `websocket::client::Sender`
Ok, let's plug that in:
sender: websocket::client::Sender,
That gives:
46 | sender: websocket::client::Sender,
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 type arguments, found 0
Ok, that's a generic type. Next try:
sender: websocket::client::Sender<()>,
Finally it gives us the real type:
74 | sender:sender,
| ^^^^^^ expected (), found enum `websocket::WebSocketStream`
So finally we can finish SocketWriter:
struct SocketWriter {
my_rx: mpsc::Receiver<String>,
sender: websocket::client::Sender<websocket::WebSocketStream>,
}
There's a following compile error since the connection you get is a Result<> so you need to check for errors (it compiles if I change to self.handshake(connection.unwrap()), but that's obviously not the best practice.