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
Related
I wrote a binary tree of i32. I want to change its root node to left node. But always failed. How to do it?
fn left(&mut self) -> Result<(), Error> {
match self.root.as_mut() {
Some(root) => match root.left {
Some(left) => {
self.root = Some(left); // this line always failed
return Ok(());
}
None => {
return Err(Error::NotFound);
}
},
None => {
return Err(Error::EmptyTree);
}
}
}
self.root = Some(left) I think it is easy to do this, but always failed.
error[E0507]: cannot move out of `root.left.0` which is behind a mutable reference
--> src/main.rs:120:33
|
120 | Some(root) => match root.left {
| ^^^^^^^^^ help: consider borrowing here: `&root.left`
121 | Some(left) => {
| ----
| |
| data moved here
| move occurs because `left` has type `Box<Node>`, which does not implement the `Copy` trait
For more information about this error, try `rustc --explain E0507`.
playground
You can use std::mem::take, which allows taking things out of a mutable reference to them (replacing the pointed value with the default value of that type). That is, your function left can be rewritten:
use std::mem::take;
fn left(&mut self) -> Result<(), Error> {
let root = self.root.as_mut().ok_or(Error::EmptyTree)?;
let left = take(&mut root.left).ok_or(Error::NotFound)?;
*root = left;
Ok(())
}
Edit: turns out there is a method that does exactly that. I'll leave my first snippet so that you understand what happens, but the following is probably more suitable in real code.
fn left(&mut self) -> Result<(), Error> {
let root = self.root.as_mut().ok_or(Error::EmptyTree)?;
let left = root.left.take().ok_or(Error::NotFound)?;
*root = left;
Ok(())
}
I'm trying to write an async function that will traverse the filesystem tree, recursively, and calls an asynchronous callback for each file found.
This is for a learning effort, I have no real use case.
Here is what I have so far:
use async_std::{
fs::{self, *},
path::*,
prelude::*,
}; // 1.5.0, features = ["unstable"]
use futures::{
executor::block_on,
future::{BoxFuture, FutureExt},
}; // 0.3.4
use std::{marker::Sync, pin::Pin};
fn main() {
fn walkdir<F>(path: String, cb: &'static F) -> BoxFuture<'static, ()>
where
F: Fn(&DirEntry) -> BoxFuture<()> + Sync + Send,
{
async move {
let mut entries = fs::read_dir(&path).await.unwrap();
while let Some(path) = entries.next().await {
let entry = path.unwrap();
let path = entry.path().to_str().unwrap().to_string();
if entry.path().is_file().await {
cb(&entry).await
} else {
walkdir(path, cb).await
}
}
}
.boxed()
}
let foo = async {
walkdir(".".to_string(), &|entry: &DirEntry| async {
async_std::println!(">> {}\n", &entry.path().to_str().unwrap()).await
})
.await
};
block_on(foo);
}
I get this far by some sort of trial and error, but now I'm stuck on async closure callback with this error
warning: unused import: `path::*`
--> src/main.rs:3:5
|
3 | path::*,
| ^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: unused import: `pin::Pin`
--> src/main.rs:10:25
|
10 | use std::{marker::Sync, pin::Pin};
| ^^^^^^^^
error[E0308]: mismatched types
--> src/main.rs:33:54
|
33 | walkdir(".".to_string(), &|entry: &DirEntry| async {
| ______________________________________________________^
34 | | async_std::println!(">> {}\n", &entry.path().to_str().unwrap()).await
35 | | })
| |_________^ expected struct `std::pin::Pin`, found opaque type
|
= note: expected struct `std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = ()> + std::marker::Send>>`
found opaque type `impl core::future::future::Future`
use async_std::{
fs::{self, *},
path::*,
prelude::*,
}; // 1.5.0
use futures::{future::{Future, FutureExt, LocalBoxFuture}, executor}; // 0.3.4
fn main() {
async fn walkdir<R>(path: impl AsRef<Path>, mut cb: impl FnMut(DirEntry) -> R)
where
R: Future<Output = ()>,
{
fn walkdir_inner<'a, R>(path: &'a Path, cb: &'a mut dyn FnMut(DirEntry) -> R) -> LocalBoxFuture<'a, ()>
where
R: Future<Output = ()>,
{
async move {
let mut entries = fs::read_dir(path).await.unwrap();
while let Some(path) = entries.next().await {
let entry = path.unwrap();
let path = entry.path();
if path.is_file().await {
cb(entry).await
} else {
walkdir_inner(&path, cb).await
}
}
}.boxed_local()
}
walkdir_inner(path.as_ref(), &mut cb).await
}
executor::block_on({
walkdir(".", |entry| async move {
async_std::println!(">> {}", entry.path().display()).await
})
});
}
Notable changes:
Take in AsRef<Path> instead of a String and a generic closure instead of a trait object reference
Change the closure type to be FnMut as it's more permissive
The closure returns any type that is a future.
There's an inner implementation function that hides the ugly API required for recursive async functions.
The callback takes the DirEntry by value instead of by reference.
See also:
How to asynchronously explore a directory and its sub-directories?
How to using async fn callback in 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.
This is a simplified version of the issue I am currently facing.
trait SuperObject {
fn object_name(&self) -> String;
}
trait Inspect {
fn inspect(&self);
}
impl Inspect for SuperObject {
fn inspect(&self) {
println!("I am a Superobject.");
}
}
struct Object {
name: String
}
impl SuperObject for Box<Object> {
fn object_name(&self) -> String {
format!("I am {}.", self.name.clone())
}
}
struct ObjectPool {
object1: Box<Object>,
object2: Box<Object>,
object3: Box<Object>
}
impl ObjectPool {
pub fn new() -> ObjectPool {
ObjectPool {
object1: Box::new(Object { name: String::from("Object 1") }),
object2: Box::new(Object { name: String::from("Object 2") }),
object3: Box::new(Object { name: String::from("Object 3") })
}
}
fn all_objects(&self) -> Vec<&SuperObject> {
let mut ret: Vec<&SuperObject> = Vec::new();
ret.push(&self.object1);
ret.push(&self.object2);
ret.push(&self.object3);
ret
}
}
fn main() {
let objectpool: ObjectPool = ObjectPool::new();
let allobjects: Vec<&SuperObject> = objectpool.all_objects();
for i in &allobjects {
println!("{}", i.object_name());
// Comment the following line in order to drop error E0597
i.inspect(); // FIXME: borrowed value must be valid for the static lifetime
}
}
The error when attempting to compile this snippet is as follows:
error[E0597]: `objectpool` does not live long enough
--> src/main.rs:50:41
|
50 | let allobjects: Vec<&SuperObject> = objectpool.all_objects();
| ^^^^^^^^^^ does not live long enough
...
56 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
error: aborting due to previous error
After numerous searches, from what I understand, the objects being instantiated have a default static lifetime, as referred in https://doc.rust-lang.org/book/second-edition/ch19-02-advanced-lifetimes.html
I believe the output of ObjectPool's all_objects method is elided by the compiler as static as is evidenced by one of the errors evoked when I attempted to debug the snippet:
error[E0308]: mismatched types
--> src/main.rs:42:18
|
42 | ret.push(&self.object2);
| ^^^^^^^^^^^^^ expected struct `std::boxed::Box`, found reference
|
= note: expected type `std::boxed::Box<SuperObject>`
found type `&std::boxed::Box<SuperObject + 'static>`
What would be the best course of action this doesn't involve scrapping the object pool altogether? Or is there a more elegant abstraction befitting for rust implementations?
The issue is your impl Inspect for SuperObject. Implementing a trait for another trait does not do what you expect from it. Basically the rule is: never do it. Essentially it means that only when you have a &(SuperObject + 'static), you'll be able to treat it as an Inspect. What you want is
impl<T: SuperObject + ?Sized> Inspect for T {
fn inspect(&self) {
println!("I am a Superobject.");
}
}
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(),
}
}