Converting a future result in another future - rust

I have a function that returns a future with a User trait. I have two concrete implementations of it: AnonymousUser and BaseUser. To get the BaseUser, after authentication, I have to go to the database and fetch it, which may or not succeed, and return the new future with the correct type. I've tried the following (playground):
extern crate futures; // 0.1.23
extern crate rand; // 0.5.4
use futures::future::{ok, Future};
use std::io::Error;
trait User {}
#[derive(Debug)]
struct AnonymousUser;
impl User for AnonymousUser {}
#[derive(Debug)]
struct BaseUser;
impl User for BaseUser {}
fn fetch_base_user() -> impl Future<Item = BaseUser, Error = Error> {
ok(BaseUser)
}
fn run_future() -> impl Future<Item = impl User, Error = Error> {
match rand::random::<bool>() {
true => fetch_base_user().from_err().then(move |res| match res {
Ok(user) => ok(user),
Err(_) => ok(AnonymousUser),
}),
false => ok(AnonymousUser),
}
}
fn main() {
run_future().and_then(move |user| println!("User {:?}", user));
}
this failed because the return of the then function expects a BaseUser:
error[E0308]: match arms have incompatible types
--> src/main.rs:23:62
|
23 | true => fetch_base_user().from_err().then(move |res| match res {
| ______________________________________________________________^
24 | | Ok(user) => ok(user),
25 | | Err(_) => ok(AnonymousUser),
| | ----------------- match arm with an incompatible type
26 | | }),
| |_________^ expected struct `BaseUser`, found struct `AnonymousUser`
|
= note: expected type `futures::FutureResult<BaseUser, _>`
found type `futures::FutureResult<AnonymousUser, _>`
I tried forcing the return type:
use futures::future::FutureResult;
fn run_future() -> impl Future<Item=impl User, Error=Error> {
match rand::random::<bool>() {
true => fetch_base_user().from_err().then(move |res| ->
FutureResult<impl User, Error> { // Forcing the result type here
match res {
Ok(user) => ok(user),
Err(_) => ok(AnonymousUser),
}
}),
false => ok(AnonymousUser),
}
}
which fails with:
error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
--> src/main.rs:27:22
|
27 | FutureResult<impl User, Error> { // Forcing the result type here
| ^^^^^^^^^
I've tried to rework using Boxes for the return, which almost worked (playground)
fn run_future() -> impl Future<Item = Box<impl User>, Error = Error> {
match rand::random::<bool>() {
true => fetch_base_user()
.from_err()
.then(move |res| -> FutureResult<Box<User>, Error> {
match res {
Ok(user) => ok(Box::new(user) as Box<User>),
Err(_) => ok(Box::new(AnonymousUser) as Box<User>),
}
}),
false => ok(Box::new(AnonymousUser) as Box<User>),
}
}
which fails with
error[E0308]: match arms have incompatible types
--> src/main.rs:22:5
|
22 | / match rand::random::<bool>() {
23 | | true => fetch_base_user().from_err().then(move |res| match res {
24 | | Ok(user) => ok(Box::new(user) as Box<User>),
25 | | Err(_) => ok(Box::new(AnonymousUser) as Box<User>),
26 | | }),
27 | | false => ok(Box::new(AnonymousUser) as Box<User>),
| | ---------------------------------------- match arm with an incompatible type
28 | | }
| |_____^ expected struct `futures::Then`, found struct `futures::FutureResult`
|
= note: expected type `futures::Then<futures::future::FromErr<impl futures::Future, _>, futures::FutureResult<std::boxed::Box<User>, _>, [closure#src/main.rs:23:51: 26:10]>`
found type `futures::FutureResult<std::boxed::Box<User>, _>`
So I guess it's only a matter of forcing both to be the same result type

At the end the comment from shepmaster led me to the response through this other question: How do I conditionally return different types of futures?
Basically using Either::A and Either::B solves the issue. I still couldn't make it work without boxing the parameters but this might be a different question.

Related

Cannot infer an appropriate lifetime due to conflicting requirements

I am new to rust and I am building a TUI app using rust-tui to practice and understand the concepts of rust. I have this code:
// the widgets that can be renderd on the screen
#[derive(Clone)]
pub enum Widgets<'a> {
ResList(ResList<'a>),
ListResults(ListResults<'a>),
Input(Input),
}
pub struct Screen<'a> {
renders_done: u32,
tx: Sender<Result<Event, crossterm::ErrorKind>>,
rx: Receiver<Result<Event, crossterm::ErrorKind>>,
main_screen: Widgets<'a>,
}
impl Screen<'_> {
pub async fn handle_events(&mut self) {
let event = self
.rx
.recv()
.expect("Err while recievent the events in the reciever")
.unwrap();
let new_screen: Option<Widgets> = match &mut self.main_screen {
Widgets::ResList(res_list) => {
match event {
Event::Key(event) => match event.code {
KeyCode::Esc => {
Screen::exit_app();
None
}
_ => None,
}
}
Widgets::Input(input) => input.handle_events(event).await, <-- the problem comes when I add this
_ => None,
};
match new_screen {
Some(screen) => self.main_screen = screen,
None => {}
}
}
}
impl Input {
async fn handle_events(&mut self, event: Event) -> Option<Widgets> {
None
}
}
The idea is that if a sub-module returns a widget the main screen should be changed to that new widget. For texting purposes for now I never return a widget.
But when I try and build the code the compiler complains:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/model/tui/screen.rs:84:32
|
84 | pub async fn handle_events(&mut self) {
| ^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the method body at 84:32...
--> src/model/tui/screen.rs:84:32
|
84 | pub async fn handle_events(&mut self) {
| ^
note: ...so that the expression is assignable
--> src/model/tui/screen.rs:84:32
|
84 | pub async fn handle_events(&mut self) {
| ^^^^^^^^^
= note: expected `&mut Screen<'_>`
found `&mut Screen<'_>`
note: but, the lifetime must be valid for the lifetime `'_` as defined on the impl at 45:13...
--> src/model/tui/screen.rs:45:13
|
45 | impl Screen<'_> {
| ^^
note: ...so that the expression is assignable
--> src/model/tui/screen.rs:126:52
|
126 | Some(mut screen) => self.main_screen = screen,
| ^^^^^^
= note: expected `Widgets<'_>`
found `Widgets<'_>`
error: aborting due to previous error; 8 warnings emitted
For more information about this error, try `rustc --explain E0495`.
From what I understand the lifetimes are not living enough to be saved in the struct but I am not using references anywhere they are all owned values. Can someone help me understand what am I missing?
Specify the lifetimes explicitly on the widget returned and create an explicit lifetime of the object:
impl<'screen> Screen<'screen> {
pub async fn handle_events<'c>(&'cmut self) {
let event = self
.rx
.recv()
.expect("Err while recievent the events in the reciever")
.unwrap();
let new_screen: Option<Widgets<'screen>> = match &mut self.main_screen {
Widgets::ResList(res_list) => {
match event {
Event::Key(event) => match event.code {
KeyCode::Esc => {
Screen::exit_app();
None
}
_ => None,
}
}
Widgets::Input(input) => input.handle_events(event).await,
_ => None,
};
match new_screen {
Some(screen) => self.main_screen = screen,
None => {}
}
}
}
Also add the lifetime explicitly when returning a Widget from inside a function

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 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 {})
}
})
}

What does "expected type `()`" mean on a match expression?

I'm rewriting a simple TCP based server to experiment with Rust. It should retrieve input of an client and then match that input to run a function:
use std::io::BufRead;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Write;
use std::net::{TcpListener, TcpStream};
use std::thread;
fn handle_connection(stream: TcpStream) {
let stream_clone = stream.try_clone().unwrap();
let mut reader = BufReader::new(stream);
let mut writer = BufWriter::new(stream_clone);
loop {
let mut s = String::new();
reader.read_line(&mut s).unwrap();
match s.as_str() {
//"test" => writer.write(s.as_bytes()).unwrap();
"test" => writer.write(b"test successfull").unwrap(),
_ => writer.write(b"Command not recognized...").unwrap(),
}
writer.flush().unwrap();
}
}
fn main() {
let listener = TcpListener::bind("127.0.0.1:8888").unwrap();
for stream in listener.incoming() {
thread::spawn(move || {
handle_connection(stream.unwrap());
});
}
}
And the error:
error[E0308]: mismatched types
--> src/main.rs:16:9
|
16 | / match s.as_str() {
17 | | //"test" => writer.write(s.as_bytes()).unwrap();
18 | | "test" => writer.write(b"test successfull").unwrap(),
19 | | _ => writer.write(b"Command not recognized...").unwrap(),
20 | | }
| |_________^ expected (), found usize
|
= note: expected type `()`
found type `usize`
My main problem now is to check the retrieved bytes if they belong to an match and I'm not quite sure how to achieve that.
I couldn't find a fix for this online, rustc --explain didn't help me either
Add a semicolon after your match expression.
The type of all of the match arms is usize, so the resulting type of the match is also a usize. Your code is effectively
fn main() {
{
42
}
println!("Hi");
}
error[E0308]: mismatched types
--> src/main.rs:3:9
|
3 | 42
| ^^ expected `()`, found integer
See also:
Why don't we add a semicolon (;) at the end of if/else?
Are semicolons optional in Rust?

Lifetime issue with CpuPool

I'm writing a simple RPC server with tokio and futures-cpupool. The server holds a BTreeMap of boxed closures, with the function name as key. The current implementation is pretty straight-forward:
pub struct SlackerServiceSync<T>
where T: Send + Sync + 'static
{
functions: Arc<BTreeMap<String, RpcFnSync<T>>>,
pool: CpuPool,
}
impl<T> SlackerServiceSync<T>
where T: Send + Sync + 'static
{
pub fn new(functions: Arc<BTreeMap<String, RpcFnSync<T>>>,
threads: usize)
-> SlackerServiceSync<T> {
let pool = CpuPool::new(threads);
SlackerServiceSync { functions, pool }
}
}
impl<T> Service for SlackerServiceSync<T>
where T: Send + Sync + 'static
{
type Request = SlackerPacket<T>;
type Response = SlackerPacket<T>;
type Error = io::Error;
type Future = BoxFuture<Self::Response, Self::Error>;
fn call(&self, req: Self::Request) -> Self::Future {
match req {
SlackerPacket::Request(sreq) => {
debug!("getting request: {:?}", sreq.fname);
if let Some(f) = self.functions.get(&sreq.fname) {
self.pool
.spawn_fn(move || -> FutureResult<T, Self::Error> {
ok(f(&sreq.arguments))
})
.and_then(move |result| {
debug!("getting results");
ok(SlackerPacket::Response(SlackerResponse {
version: sreq.version,
code: RESULT_CODE_SUCCESS,
content_type: sreq.content_type,
serial_id: sreq.serial_id,
result: result,
}))
})
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Oneshot canceled"))
.boxed()
} else {
let error = SlackerError {
version: sreq.version,
code: RESULT_CODE_NOT_FOUND,
serial_id: sreq.serial_id,
};
ok(SlackerPacket::Error(error)).boxed()
}
}
SlackerPacket::Ping(ref ping) => {
ok(SlackerPacket::Pong(SlackerPong { version: ping.version })).boxed()
}
_ => err(io::Error::new(io::ErrorKind::InvalidInput, "Unsupported packet")).boxed(),
}
}
}
I'm currently blocked by this lifetime issue on self.functions.get(&sreq.fname).
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> src/service.rs:103:49
|
103 | if let Some(f) = self.functions.get(&sreq.fname) {
| ^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 99:55...
--> src/service.rs:99:56
|
99 | fn call(&self, req: Self::Request) -> Self::Future {
| ________________________________________________________^
100 | | match req {
101 | | SlackerPacket::Request(sreq) => {
102 | | debug!("getting request: {:?}", sreq.fname);
... |
133 | | }
134 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/service.rs:103:34
|
103 | if let Some(f) = self.functions.get(&sreq.fname) {
| ^^^^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `[closure#src/service.rs:105:35: 107:36 f:&std::boxed::Box<for<'r> std::ops::Fn(&'r std::vec::Vec<T>) -> T + std::marker::Send + std::marker::Sync>, sreq:packets::SlackerRequest<T>]` will meet its required lifetime bounds
--> src/service.rs:105:26
|
105 | .spawn_fn(move || -> FutureResult<T, Self::Error> {
| ^^^^^^^^
Similar code works without CpuPool. I cannot fully understand the error reported by compiler.
Full code is here
It turns out I need to wrap the closure into an Arc by declaring the RcpFnSync like this:
pub type RpcFnSync<T> = Arc<Fn(&Vec<T>) -> T + Send + Sync + 'static>;
Then clone it before sending to another thread:
fn call(&self, req: Self::Request) -> Self::Future {
match req {
SlackerPacket::Request(sreq) => {
debug!("getting request: {:?}", sreq.fname);
if let Some(fi) = self.functions.get(&sreq.fname) {
let f = fi.clone();
self.pool
.spawn_fn(move || -> FutureResult<Self::Response, Self::Error> {
let result = f(&sreq.arguments);
ok(SlackerPacket::Response(SlackerResponse {
version: sreq.version,
code: RESULT_CODE_SUCCESS,
content_type: sreq.content_type,
serial_id: sreq.serial_id,
result: result,
}))
})
.boxed()
} else {
let error = SlackerError {
version: sreq.version,
code: RESULT_CODE_NOT_FOUND,
serial_id: sreq.serial_id,
};
ok(SlackerPacket::Error(error)).boxed()
}
}
SlackerPacket::Ping(ref ping) => {
ok(SlackerPacket::Pong(SlackerPong { version: ping.version })).boxed()
}
_ => err(io::Error::new(io::ErrorKind::InvalidInput, "Unsupported packet")).boxed(),
}
}

Resources