Define function based on match - rust

I have the following function, which runs fine:
use chrono_tz::Tz;
use chrono::{TimeZone, NaiveDate, NaiveDateTime};
fn my_func(time_zone: &str, fmt: &str, ndt: NaiveDateTime) {
let datefmt_func = |ndt: NaiveDateTime| time_zone.parse::<Tz>().unwrap().from_utc_datetime(&ndt).format(fmt);
let res = datefmt_func(ndt).to_string();
println!("res: {res}");
}
fn main() {
let time_zone = "UTC";
let fmt = "%Y-%m-%d %H:%M:%S%z";
let ndt = NaiveDate::from_ymd_opt(2018, 9, 28).unwrap().and_hms_opt(2, 30, 0).unwrap();
my_func(time_zone, fmt, ndt);
}
I'd like to let it accept None as time_zone. Within it, I need to repeatedly call datefmt_func, so I'd like to assign to datefmt_func once and reuse it. Here's what I've tried:
use chrono_tz::Tz;
use chrono::{TimeZone, NaiveDate, NaiveDateTime};
fn my_func(time_zone: Option<&str>, fmt: &str, ndt: NaiveDateTime) {
let datefmt_func = match time_zone {
Some(time_zone) => |ndt: NaiveDateTime| time_zone.parse::<Tz>().unwrap().from_utc_datetime(&ndt).format(fmt),
None => |ndt: NaiveDateTime| ndt.format(fmt),
};
let res = datefmt_func(ndt).to_string();
println!("res: {res}");
}
fn main() {
let time_zone = Some("UTC");
let fmt = "%Y-%m-%d %H:%M:%S%z";
let ndt = NaiveDate::from_ymd_opt(2018, 9, 28).unwrap().and_hms_opt(2, 30, 0).unwrap();
my_func(time_zone, fmt, ndt);
}
However, this gives me
error[E0308]: `match` arms have incompatible types
--> src/main.rs:7:17
|
5 | let datefmt_func = match time_zone {
| ________________________-
6 | | Some(time_zone) => |ndt: NaiveDateTime| time_zone.parse::<Tz>().unwrap().from_utc_datetime(&ndt).format(fmt),
| | -----------------------------------------------------------------------------------------
| | |
| | the expected closure
| | this is found to be of type `[closure#src/main.rs:6:28: 6:48]`
7 | | None => |ndt: NaiveDateTime| ndt.format(fmt),
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
8 | | };
| |_____- `match` arms have incompatible types
|
= note: expected closure `[closure#src/main.rs:6:28: 6:48]`
found closure `[closure#src/main.rs:7:17: 7:37]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
For more information about this error, try `rustc --explain E0308`.
error: could not compile `tmp` due to previous error
How can I adjust my code so that it will run? If "no two closures, even if identical, have the same type", then how can I assign a function which depends on a local variable (time_zone) to datefmt_func?

Do as the compiler tells you, use trait objects and Box the closures:
fn my_func(time_zone: Option<&str>, fmt: &str, ndt: NaiveDateTime) {
let datefmt_func: Box<dyn Fn(_) -> _> = match time_zone {
Some(time_zone) => Box::new(|ndt: NaiveDateTime| {
time_zone
.parse::<Tz>()
.unwrap()
.from_utc_datetime(&ndt)
.format(fmt)
}),
None => Box::new(|ndt: NaiveDateTime| ndt.format(fmt)),
};
let res = datefmt_func(ndt).to_string();
println!("res: {res}");
}
Or in your simple example just run the code instead of creating a closure which you immediately call:
fn my_func(time_zone: Option<&str>, fmt: &str, ndt: NaiveDateTime) {
let res = match time_zone {
Some(time_zone) => time_zone
.parse::<Tz>()
.unwrap()
.from_utc_datetime(&ndt)
.format(fmt),
None => ndt.format(fmt),
}
.to_string();
println!("res: {res}");
}

Related

rust async trait with coroutine

I develop an update system in rust. It create patches between 2 binaries. You can download binaries or patches and install it. Patches and binaries are compressed. The client download compressed files and decompress it
I do
#[async_trait]
impl<'a> Stream for UpdatePackageStream<'a> {
type Item = Result<SharedUpdateProgress, UpdateError>;
async fn poll_next(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
let download_poll = this.download_stream.poll_next_unpin(cx);
let apply_poll = this.apply_stream.poll_next_unpin(cx);
match (download_poll, apply_poll) {
(Poll::Ready(None), Poll::Ready(None)) => Poll::Ready(None),
(Poll::Pending, Poll::Pending) => Poll::Pending,
(Poll::Pending, Poll::Ready(None)) => Poll::Pending,
(Poll::Ready(None), Poll::Pending) => Poll::Pending,
(Poll::Ready(Some(Err(err))), _) => {
// Download errors cause the apply thread to be cancelled
this.apply_stream.cancel();
Poll::Ready(Some(Err(err)))
}
(download_poll, apply_poll) => {
let mut delta = Progression::default();
if let Poll::Ready(Some(Ok(download_progress))) = download_poll {
this.state.borrow_mut().available = download_progress.available;
let mut state = this.shared_state.lock().await;
state.downloading_operation_idx = download_progress.available.operation_idx;
delta.downloaded_files = download_progress.delta_downloaded_files;
delta.downloaded_bytes = download_progress.delta_downloaded_bytes;
this.apply_stream.notify(download_progress.available);
}
if let Poll::Ready(Some(apply_progress)) = apply_poll {
match apply_progress {
Ok(apply_progress) => {
this.state.borrow_mut().applied.operation_idx =
apply_progress.operation_idx;
let mut state = this.shared_state.lock().await; // note the await here, so the closure must be async
state.applying_operation_idx = apply_progress.operation_idx;
delta.applied_files = apply_progress.delta_applied_files;
delta.applied_input_bytes = apply_progress.delta_input_bytes;
delta.applied_output_bytes = apply_progress.delta_output_bytes;
}
Err(ApplyError::OperationFailed { path, slice, cause }) => {
warn!("{} failed: {}", path, cause);
let mut state = this.state.borrow_mut();
state.failures.push(match slice {
Some(slice) => metadata::v1::Failure::Slice { path, slice },
None => metadata::v1::Failure::Path { path },
});
delta.failed_files = 1;
}
Err(ApplyError::Cancelled) => {}
Err(ApplyError::PoisonError) => {
return Poll::Ready(Some(Err(UpdateError::PoisonError)))
}
}
}
`` {
let mut state = this.shared_state.lock().await;
state.inc_progress(delta);
}
Poll::Ready(Some(Ok(this.shared_state.clone())))
}
}
}
}
But get
error[E0195]: lifetime parameters or bounds on method `poll_next` do not match the trait declaration
--> lib/src/workspace/updater.rs:143:14
|
143 | async fn poll_next(
| ______________^
144 | | self: Pin<&mut Self>,
145 | | cx: &mut std::task::Context<'_>,
146 | | ) -> Poll<Option<Self::Item>> {
| |_____^ lifetimes do not match method in trait
Stream is from future::stream::Stream
Without async_trait and the 2 async fn, the fn and my app build without issue.
If I use std::sync::Mutex instead of tokio::sync::Mutex, I get
let state = update_state.lock();
| ----- has type `std::sync::MutexGuard<'_, UpdateProgress>` which is not `Send`
...
273 | let res = update_stream.try_for_each(|_state| future::ready(Ok(()))).await;
| ^^^^^^ await occurs here, with `state` maybe used later
...
305 | }
| - `state` is later dropped here
= note: required for the cast from `impl futures::Future<Output = Result<tonic::Response<BuildOutput>, Status>>` to the object type `dyn futures::Future<Output = Result<tonic::Response<BuildOutput>, Status>> + std::marker::Send`
UpdateProgress is a simple struct.
I don't inderstand why I get these issues and don't know how to fix them

What signature can I use to download files using Axum and Tokio?

I'm using axum and this code (found here) to download files:
use axum::{
body::StreamBody,
http::{header, StatusCode},
response::{Headers, IntoResponse},
routing::get,
Router,
};
use std::net::SocketAddr;
use tokio_util::io::ReaderStream;
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(handler));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn handler() -> impl IntoResponse {
// `File` implements `AsyncRead`
let file = match tokio::fs::File::open("Cargo.toml").await {
Ok(file) => file,
Err(err) => return Err((StatusCode::NOT_FOUND, format!("File not found: {}", err))),
};
// convert the `AsyncRead` into a `Stream`
let stream = ReaderStream::new(file);
// convert the `Stream` into an `axum::body::HttpBody`
let body = StreamBody::new(stream);
let headers = Headers([
(header::CONTENT_TYPE, "text/toml; charset=utf-8"),
]);
Ok((headers, body))
}
Everything works. But I cannot find a way to move the below code in a separate function:
let file = match tokio::fs::File::open("Cargo.toml").await {
Ok(file) => file,
Err(err) => return Err((StatusCode::NOT_FOUND, format!("File not found: {}", err))),
};
I would like to use both tokio::fs::File and https://crates.io/crates/rust-s3 methods in this function.
So I need a "common type" which appear to be AsyncRead, I think.
What should be the signature of the function?
I tried with:
use tokio::io::AsyncRead;
pub struct Player {
db: Arc<DB>
}
impl Handler {
pub async fn player_pdf(
&self,
id: &str,
) -> Result<&(dyn AsyncRead)> {
//...use id here...
let file = &tokio::fs::File::open("player.pdf").await?;
Ok(file)
}
}
but I get the error:
error[E0308]: mismatched types
|
55 | Ok(file)
| -- ^^^^
| | |
| | expected reference, found struct `tokio::fs::File`
| | help: consider borrowing here: `&file`
| arguments to this enum variant are incorrect
|
= note: expected reference `&dyn tokio::io::AsyncRead`
found struct `tokio::fs::File`
I tried with: let file = &tokio::fs::File::open("player.pdf").await?; and I got:
error[E0515]: cannot return value referencing temporary value
|
43 | let file = &tokio::fs::File::open(...
| --------------------------- temporary value created here
...
55 | Ok(file)
| ^^^^^^^^ returns a value referencing data owned by the current function
What can I use?
Returning a generic "boxed" value might be the solution here:
impl Handler {
pub async fn player_pdf(
&self,
id: &str,
) -> Result<Box<dyn AsyncRead>> {
//...use id here...
Ok(Box::new(tokio::fs::File::open("player.pdf").await?))
}
}
Where now there's no dangling reference, it's encapsulated and fully owned.

Why does a match statement without a trailing semicolon cause an error? [duplicate]

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?

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?

Converting a future result in another future

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.

Resources