How to conditionally run one of two functions with tokio::join? - asynchronous

I need to run 4 functions concurrently but one of them is different based on user input.
If I use "if-else" I get "if and else have incompatible types" due to Future.
The only way I see is to make a third function that selects from the other two, but it doesn't allow (to my knowledge) to run concurrently due to awaiting.
Another way is to make two different join! but that seems "code expensive".
What should I do in this situation?
tokio::join!(
self.func1(),
if self.flag() {
self.func2()
} else {
self.func3()
},
self.func4(),
self.func5()
);
The function signature is as follows:
pub async fn funcN(&self) -> Result<Vec<String>> {

You can use if inside an async block to produce a single future type from two:
tokio::join!(
func1(),
async {
if flag() {
func2().await
} else {
func3().await
}
},
func4(),
func5()
);
This has slightly different scheduling than the other answers: the flag() call is made part of the future rather than run immediately. If this is undesirable, you could put the boolean result in a variable beforehand.
Other than that, this is much like the Either approach, but can generalize to more than two choices.

The probably easiest way is to use a Box and dynamic dispatch. For example, this compiles:
tokio::join!(
func1(),
if flag() {
Box::pin(func2()) as Pin<Box<dyn Future<Output = _>>>
} else {
Box::pin(func3()) as Pin<Box<dyn Future<Output = _>>>
},
func4(),
func5()
);
Playground

You can use the futures::future::Either enum:
tokio::join!(
self.func1(),
if self.flag() {
Either::Left(self.func2())
} else {
Either::Right(self.func3())
},
self.func4(),
self.func5()
);
Playground

Related

How to use Rust futures in callbacks?

Is there any way to use futures in callbacks? For example...
// Send message on multiple channels while removing ones that are closed.
use smol::channel::Sender;
...
// (expecting bool, found opaque type)
vec_of_sender.retain( |sender| async {
sender.send(msg.clone()).await.is_ok()
});
My work-around is to loop twice: On the first pass I delete closed senders (non-async) and on the second I do the actual send (async using for sender in ...). But it seems like I should be able to do it all in a single retain() call.
You can't use retain in this way. The closure that retain accepts must implement FnMut(&T) -> bool, but every async function returns an implementation of Future.
You can turn an async function into a synchronous one by blocking on it. For example, if you were using tokio, you could do this:
use tokio::runtime::Runtime;
let rt = Runtime::new().unwrap();
vec_of_sender.retain(|sender| {
rt.block_on(async { sender.send().await.is_ok() })
});
However, there is overhead to adding an async runtime, and I have a feeling that you are trying to solve the wrong problem.
The closure passed to retain must return a bool, but every async function returns impl Future. Instead, you can use Stream, which is the asynchronous version of Iterator. You can convert the vector into a Stream:
let stream = stream::iter(vec_of_sender);
And then use the filter method, which accepts an asynchronous closure and returns a new Stream:
let vec_of_sender = stream.filter(|sender| async {
sender.send(msg.clone()).await.is_ok()
}).collect::<Vec<Sender>>();
To avoid creating a new Vec, you can also use swap_remove:
let mut i = 0usize;
while i < vec_of_sender.len() {
if vec_of_sender[i].send(msg.clone()).await.is_ok() {
i += 1;
} else {
vec_of_sender.swap_remove(i);
}
}
Note that this will change the order of the vector.

Should I return await in Rust?

In JavaScript, async code is written with Promises and async/await syntax similar to that of Rust. It is generally considered redundant (and therefore discouraged) to return and await a Promise when it can simply be returned (i.e., when an async function is executed as the last thing in another function):
async function myFn() { /* ... */ }
async function myFn2() {
// do setup work
return await myFn()
// ^ this is not necessary when we can just return the Promise
}
I am wondering whether a similar pattern applies in Rust. Should I prefer this:
pub async fn my_function(
&mut self,
) -> Result<()> {
// do synchronous setup work
self.exec_command(
/* ... */
)
.await
}
Or this:
pub fn my_function(
&mut self,
) -> impl Future<Output = Result<()>> {
// do synchronous setup work
self.exec_command(
/* ... */
)
}
The former feels more ergonomic to me, but I suspect that the latter might be more performant. Is this the case?
One semantic difference between the two variants is that in the first variant the synchronous setup code will run only when the returned future is awaited, while in the second variant it will run as soon as the function is called:
let fut = x.my_function();
// in the second variant, the synchronous setup has finished by now
...
let val = fut.await; // in the first variant, it runs here
For the difference to be noticeable, the synchronous setup code must have side effects, and there needs to be a delay between calling the async function and awaiting the future it returns.
Unless you have specific reason to execute the preamble immediately, go with the async function, i.e. the first variant. It makes the function slightly more predictable, and makes it easier to add more awaits later as the function is refactored.
There is no real difference between the two since async just resolves down to impl Future<Output=Result<T, E>>. I don't believe there is any meaningful performance difference between the two, at least in my empirical usage of both.
If you are asking for preference in style then in my opinion the first one is preferred as the types are clearer to me and I agree it is more ergonomic.

How to replace multiple "if-else-if" with a map

I am getting one concern on multi layer if-else-if condition so I want to make short by using a map.
Please see below code in if-else-if which I want to replace with a map.
function, args := APIstub.GetFunctionAndParameters()
if function == "queryProduce" {
return s.queryProduce(APIstub, args)
} else if function == "initLedger" {
return s.initLedger(APIstub)
} else if function == "createProduce" {
return s.createProduce(APIstub, args)
} else if function == "queryAllProduces" {
return s.queryAllProduces(APIstub)
} else if function == "changeProduceStatus" {
return s.changeProduceStatus(APIstub, args)
}
return shim.Error("Invalid Smart Contract function name.")
}
For what you have a switch would be nice:
switch function {
case "queryProduce":
return s.queryProduce(APIstub, args)
case "initLedger":
return s.initLedger(APIstub)
case "createProduce":
return s.createProduce(APIstub, args)
case "queryAllProduces":
return s.queryAllProduces(APIstub)
case "changeProduceStatus":
return s.changeProduceStatus(APIstub, args)
}
Using a map would be inconvenient because not all your methods have the same signature, but you could use multiple maps.
Yet another solution could be to use reflection to call the methods, but again, handling the different arguments would be inconvenient. Reflection is also slower, not to mention you'd have to take care of not to allow calling methods not intended to be exposed. For an example, see Call functions with special prefix/suffix.
It is possible to express what you have as a map. The basic setup here is, no matter which path you go down, you get some function that you can call with no parameters, and it always returns the same type (error). I might explicitly pass the args in.
The high-level structure of this is to have a map of function names to functions, then call the selected function.
funcMap := map[string]func([]string) error{...}
funcName, args := APIstub.GetFunctionAndParameters()
f := funcMap[funcName]
if f == nil {
f = func(_ []string) error {
return shim.Error("Invalid Smart Contract function name.")
}
}
return f(args)
The map syntax gets kind of verbose
funcMap := map[string]func([]string) error{
"queryProduce": func(args []string) error {
return s.queryProduce(APIstub, args)
},
"initLedger": func(_ []string) error {
return s.initLedger(APIstub)
},
}
The map approach is better if you're ever going to call this in multiple places, or you want a separate validation step that some name would be defined if used, or if the actual list of functions is dynamic (you can add or remove things from the map at runtime). The inconsistent method signatures do introduce a complication and making everything consistent would help here (make functions like initLedger take an argument list even if it's unused).
In ordinary code I'd expect the switch form from #icza's answer to be more idiomatic.

How do I execute an async/await function without using any external dependencies?

I am attempting to create simplest possible example that can get async fn hello() to eventually print out Hello World!. This should happen without any external dependency like tokio, just plain Rust and std. Bonus points if we can get it done without ever using unsafe.
#![feature(async_await)]
async fn hello() {
println!("Hello, World!");
}
fn main() {
let task = hello();
// Something beautiful happens here, and `Hello, World!` is printed on screen.
}
I know async/await is still a nightly feature, and it is subject to change in the foreseeable future.
I know there is a whole lot of Future implementations, I am aware of the existence of tokio.
I am just trying to educate myself on the inner workings of standard library futures.
My helpless, clumsy endeavours
My vague understanding is that, first off, I need to Pin task down. So I went ahead and
let pinned_task = Pin::new(&mut task);
but
the trait `std::marker::Unpin` is not implemented for `std::future::GenFuture<[static generator#src/main.rs:7:18: 9:2 {}]>`
so I thought, of course, I probably need to Box it, so I'm sure it won't move around in memory. Somewhat surprisingly, I get the same error.
What I could get so far is
let pinned_task = unsafe {
Pin::new_unchecked(&mut task)
};
which is obviously not something I should do. Even so, let's say I got my hands on the Pinned Future. Now I need to poll() it somehow. For that, I need a Waker.
So I tried to look around on how to get my hands on a Waker. On the doc it kinda looks like the only way to get a Waker is with another new_unchecked that accepts a RawWaker. From there I got here and from there here, where I just curled up on the floor and started crying.
This part of the futures stack is not intended to be implemented by many people. The rough estimate that I have seen in that maybe there will be 10 or so actual implementations.
That said, you can fill in the basic aspects of an executor that is extremely limited by following the function signatures needed:
async fn hello() {
println!("Hello, World!");
}
fn main() {
drive_to_completion(hello());
}
use std::{
future::Future,
ptr,
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
};
fn drive_to_completion<F>(f: F) -> F::Output
where
F: Future,
{
let waker = my_waker();
let mut context = Context::from_waker(&waker);
let mut t = Box::pin(f);
let t = t.as_mut();
loop {
match t.poll(&mut context) {
Poll::Ready(v) => return v,
Poll::Pending => panic!("This executor does not support futures that are not ready"),
}
}
}
type WakerData = *const ();
unsafe fn clone(_: WakerData) -> RawWaker {
my_raw_waker()
}
unsafe fn wake(_: WakerData) {}
unsafe fn wake_by_ref(_: WakerData) {}
unsafe fn drop(_: WakerData) {}
static MY_VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
fn my_raw_waker() -> RawWaker {
RawWaker::new(ptr::null(), &MY_VTABLE)
}
fn my_waker() -> Waker {
unsafe { Waker::from_raw(my_raw_waker()) }
}
Starting at Future::poll, we see we need a Pinned future and a Context. Context is created from a Waker which needs a RawWaker. A RawWaker needs a RawWakerVTable. We create all of those pieces in the simplest possible ways:
Since we aren't trying to support NotReady cases, we never need to actually do anything for that case and can instead panic. This also means that the implementations of wake can be no-ops.
Since we aren't trying to be efficient, we don't need to store any data for our waker, so clone and drop can basically be no-ops as well.
The easiest way to pin the future is to Box it, but this isn't the most efficient possibility.
If you wanted to support NotReady, the simplest extension is to have a busy loop, polling forever. A slightly more efficient solution is to have a global variable that indicates that someone has called wake and block on that becoming true.

Can this Rust code be written without the "match" statement?

linuxfood has created bindings for sqlite3, for which I am thankful. I'm just starting to learn Rust (0.8), and I'm trying to understand exactly what this bit of code is doing:
extern mod sqlite;
fn db() {
let database =
match sqlite::open("test.db") {
Ok(db) => db,
Err(e) => {
println(fmt!("Error opening test.db: %?", e));
return;
}
};
I do understand basically what it is doing. It is attempting to obtain a database connection and also testing for an error. I don't understand exactly how it is doing that.
In order to better understand it, I wanted to rewrite it without the match statement, but I don't have the knowledge to do that. Is that possible? Does sqlite::open() return two variables, or only one?
How can this example be written differently without the match statement? I'm not saying that is necessary or preferable, however it may help me to learn the language.
The outer statement is an assignment that assigns the value of the match expression to database. The match expression depends on the return value of sqlite::open, which probably is of type Result<T, E> (an enum with variants Ok(T) and Err(E)). In case it's Ok, the enum variant has a parameter which the match expression destructures into db and passes back this value (therefore it gets assigned to the variable database). In case it's Err, the enum variant has a parameter with an error object which is printed and the function returns.
Without using a match statement, this could be written like the following (just because you explicitly asked for not using match - most people will considered this bad coding style):
let res = sqlite::open("test.db");
if res.is_err() {
println!("Error opening test.db: {:?}", res.unwrap_err());
return;
}
let database = res.unwrap();
I'm just learning Rust myself, but this is another way of dealing with this.
if let Ok(database) = sqlite::open("test.db") {
// Handle success case
} else {
// Handle error case
}
See the documentation about if let.
This function open returns SqliteResult<Database>; given the definition pub type SqliteResult<T> = Result<T, ResultCode>, that is std::result::Result<Database, ResultCode>.
Result is an enum, and you fundamentally cannot access the variants of an enum without matching: that is, quite literally, the only way. Sure, you may have methods for it abstracting away the matching, but they are necessarily implemented with match.
You can see from the Result documentation that it does have convenience methods like is_err, which is approximately this (it's not precisely this but close enough):
fn is_err(&self) -> bool {
match *self {
Ok(_) => false,
Err(_) => true,
}
}
and unwrap (again only approximate):
fn unwrap(self) -> T {
match self {
Ok(t) => t,
Err(e) => fail!(),
}
}
As you see, these are implemented with matching. In this case of yours, using the matching is the best way to write this code.
sqlite::open() is returning an Enum. Enums are a little different in rust, each value of an enum can have fields attached to it.
See http://static.rust-lang.org/doc/0.8/tutorial.html#enums
So in this case the SqliteResult enum can either be Ok or Err if it is Ok then it has the reference to the db attached to it, if it is Err then it has the error details.
With a C# or Java background you could consider the SqliteResult as a base class that Ok and Err inherit from, each with their own relevant information. In this scenario the match clause is simply checking the type to see which subtype was returned. I wouldn't get too fixated on this parallel though it is a bad idea to try this hard to match concepts between languages.

Resources