I'm fairly new to rust programming, and I'm following the rust book. However, I recently started to make some exercises by myself to "deepen" my understanding.
I came into an issue with Warp, particularly post requests. Basically, what I am trying to do is whenever I make a post requests with two parameters, in this case 2 numbers. I want to return the sum of both as a response.
use std::collections::HashMap;
use std::ptr::hash;
use warp::{Filter};
async fn add_two_numbers(a: u32, b: u32) -> Result<impl warp::Reply, warp::Rejection> {
Ok(format!("sum {}", a+b))
}
#[tokio::main]
async fn main() {
let hello = warp::post()
.and(warp::path("add_numbers"))
.and(warp::query::<HashMap<u32, u32>>())
.and(warp::path::end())
.and_then(add_two_numbers);
warp::serve(hello)
.run(([127, 0, 0, 1], 3000))
.await
}
However, I'm stuck and do not really know how to get the parameters from the query, and pass them into the function or pass the whole HashMap to the function and from there extract the data I need.
Related
I'm learning concurrency in rust and can't find a simple example how to return and resolve a Future.
How can I implement this javascript code in rust? It seems like I have to implement the Future trait on some struct and return it (right?), but I want a straightforward example. Instead of setTimeout it can be anything, just keep it simple.
function someAsyncTask() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("result")
}, 1_000)
})
}
The most straight forward fn() -> impl Future you can create is to simply use a async function:
use std::time::Duration;
use tokio::time;
async fn some_async_task() -> &'static str {
time::sleep(Duration::from_millis(1_000)).await;
"result"
}
If you 'remove' one layer of syntax sugar this becomes the following fn returning an async block.
use std::time::Duration;
use std::future::Future;
use tokio::time;
fn some_async_task() -> impl Future<Output = &'static str> {
async {
time::sleep(Duration::from_millis(1_000)).await;
"result"
}
}
The closest analogue in Rust futures to JavaScript's Promise constructor is the oneshot channel, as seen in futures and tokio. The “sender” of the channel is analogous to the resolve function, and the “receiver” of the channel is a Future that works like the Promise does. There is no separate reject operation; if you wish to communicate errors, you can do that by passing Result in the channel.
Translating your code:
use futures::channel::oneshot;
fn some_async_task() -> oneshot::Receiver<&'static str> {
let (sender, receiver) = oneshot::channel();
set_timeout(|| { // not a real Rust function, but if it were...
sender.send("result").unwrap()
}, 1_000);
receiver
}
Note that just like you only need new Promise in JS if you are translating something callback-like into the promise world, you only need to do this in Rust if you need to cause a future to complete from an outside event. Both should be fairly rare.
Ok, so I'm very new to Rust and I'm trying to clumsily piece together a little CLI tool that makes http requests and handles the responses, by using tokio, clap, reqwest and serde.
The tool accepts a customer number as input and then it tries to fetch information about the customer. The customer may or may not have a FooBar in each country.
My code currently only works if I get a nice 200 response containing a FooBar. If I don't, the deserialization fails (naturally). (Edit: Actually, this initial assumption about the problem seems to be false, see comments below)
My aim is to only attempt the deserialization if I actually get a valid response.
How would I do that? I feel the need to see the code of a valid approach to understand this better.
Below is the entirety of my program.
use clap::Parser;
use reqwest::Response;
use serde::{Deserialize, Serialize};
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let args: Cli = Cli::parse();
let client = reqwest::Client::new();
let countries = vec!["FR", "GB", "DE", "US"];
for country in countries.iter() {
let foo_bar : FooBar = client.get(
format!("http://example-service.com/countries/{}/customers/{}/foo_bar", country, args.customer_number))
.send()
.await?
.json()
.await?;
println!("{}", foo_bar.a_value);
}
Ok(())
}
#[derive(Debug, Serialize, Deserialize)]
struct FooBar {
a_value: String,
}
#[derive(Parser, Debug)]
struct Cli {
customer_number: i32,
}
There are a few ways to approach this issue, first of all you can split the json() deserialization from send().await, i.e.:
for country in countries.iter() {
let resp: reqwest::Response = client.get(
format!("http://example-service.com/countries/{}/customers/{}/foo_bar", country, args.customer_number))
.send()
.await?;
if resp.status() != reqwest::StatusCode::OK {
eprintln!("didn't get OK status: {}", resp.status());
} else {
let foo_bar = resp.json().await?;
println!("{}", foo_bar.a_value);
}
}
If you want to keep the response body around, you can extract it through let bytes = resp.bytes().await?; and pass bytes to serde_json::from_slice(&*bytes) for the deserialization attempt.
This can be useful if you have a set of expected error response bodies.
A similar question I posted earlier is here
Rust can't modify RefCell in Rc, but completely different.
I want to simulate some natural process, so I have a Simulator, and a reactor like a NuclearReactor. The simulator will modify the reactor, and the reactor can reversely influance the simulator by modifying it. One important thing is that the NuclearReactor is wrapped from somewhere else, the solid_function must has a inmutable &self.
So after reading rust book of RefCell, I wrote something like these, It complies, but crashed.
use std::borrow::BorrowMut;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
pub struct Simulator {
nr: NuclearReactor,
data: Vec<f64>,
}
impl Simulator {
pub fn on_nuclear_data(&mut self, x: i64) {
// modify self
}
pub fn run_simulation(&mut self) {
self.nr.write_simulator();
}
}
pub struct NuclearReactor {
simulator: Option<Weak<RefCell<Simulator>>>,
}
impl NuclearReactor {
pub fn solid_function(&self, x: i64) {
/*
this function `&self` is solid, so I have to use a RefCell to wrap Simulator
*/
}
pub fn write_simulator(&self) {
/*
thread 'main' panicked at 'already borrowed: BorrowMutError'
*/
(*self.simulator.as_ref().unwrap().upgrade().unwrap()).borrow_mut().on_nuclear_data(0);
}
}
pub fn main() {
let nr_ = NuclearReactor {
simulator: None
};
let mut sm_ = Rc::new(RefCell::new(Simulator {
nr: nr_,
data: vec![],
}));
(*sm_).borrow_mut().nr.simulator = Some(Rc::downgrade(&sm_));
(*sm_).borrow_mut().run_simulation();
}
Obviously, the runtime check of borrow_mut fails.
Actually the NuclearReactor is my online code, the Simulator is an offline test, so I wanna modify the NuclearReactor at a minimal cost to let it run on the offline environment. That's why I have to keep the function solid_function with an immutable &self. Changing it to a &mut self is and then move to-modifying objects to a seperate function is feasible, but then I have to modify the online code frequently at a high cost. It there anything cool that can solve it ?
Ok, after reading this: http://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/
I finnaly realized that what I am doing is something like below and rust was helping me avoiding bugs.
let v: Vec<i64> = vec![1,2,3];
for ele in v.iter_mut(){
v.push(1);
}
Thankfully, pushing NuclearReactor's modify to a temp buffer then apply them to Simulator is just enough to solve my problem.
Also, I didn't explain the question clearly (actually I didn't get the point to describe the question until I solved it), so the community can't help me.
I'm trying to perform a parallel operation on several chunks of strings at a time, and I'm finding having an issue with the borrow checker:
(for context, identifiers is a Vec<String> from a CSV file, client is reqwest and target is an Arc<String> that is write once read many)
use futures::{stream, StreamExt};
use std::sync::Arc;
async fn nop(
person_ids: &[String],
target: &str,
url: &str,
) -> String {
let noop = format!("{} {}", target, url);
let noop2 = person_ids.iter().for_each(|f| {f.as_str();});
"Some text".into()
}
#[tokio::main]
async fn main() {
let target = Arc::new(String::from("sometext"));
let url = "http://example.com";
let identifiers = vec!["foo".into(), "bar".into(), "baz".into(), "qux".into(), "quux".into(), "quuz".into(), "corge".into(), "grault".into(), "garply".into(), "waldo".into(), "fred".into(), "plugh".into(), "xyzzy".into()];
let id_sets: Vec<&[String]> = identifiers.chunks(2).collect();
let responses = stream::iter(id_sets)
.map(|person_ids| {
let target = target.clone();
tokio::spawn( async move {
let resptext = nop(person_ids, target.as_str(), url).await;
})
})
.buffer_unordered(2);
responses
.for_each(|b| async { })
.await;
}
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e41c635e99e422fec8fc8a581c28c35e
Given chunks yields a Vec<&[String]>, the compiler complains that identifiers doesn't live long enough because it potentially goes out of scope while the slices are being referenced. Realistically this won't happen because there's an await. Is there a way to tell the compiler that this is safe, or is there another way of getting chunks as a set of owned Strings for each thread?
There was a similarly asked question that used into_owned() as a solution, but when I try that, rustc complains about the slice size not being known at compile time in the request_user function.
EDIT: Some other questions as well:
Is there a more direct way of using target in each thread without needing Arc? From the moment it is created, it never needs to be modified, just read from. If not, is there a way of pulling it out of the Arc that doesn't require the .as_str() method?
How do you handle multiple error types within the tokio::spawn() block? In the real world use, I'm going to receive quick_xml::Error and reqwest::Error within it. It works fine without tokio spawn for concurrency.
Is there a way to tell the compiler that this is safe, or is there another way of getting chunks as a set of owned Strings for each thread?
You can chunk a Vec<T> into a Vec<Vec<T>> without cloning by using the itertools crate:
use itertools::Itertools;
fn main() {
let items = vec![
String::from("foo"),
String::from("bar"),
String::from("baz"),
];
let chunked_items: Vec<Vec<String>> = items
.into_iter()
.chunks(2)
.into_iter()
.map(|chunk| chunk.collect())
.collect();
for chunk in chunked_items {
println!("{:?}", chunk);
}
}
["foo", "bar"]
["baz"]
This is based on the answers here.
Your issue here is that the identifiers are a Vector of references to a slice. They will not necessarily be around once you've left the scope of your function (which is what async move inside there will do).
Your solution to the immediate problem is to convert the Vec<&[String]> to a Vec<Vec<String>> type.
A way of accomplishing that would be:
let id_sets: Vec<Vec<String>> = identifiers
.chunks(2)
.map(|x: &[String]| x.to_vec())
.collect();
I've got the following piece of code (see playground):
use futures::{stream, Future, Stream}; // 0.1.25
use std::num::ParseIntError;
fn into_many(i: i32) -> impl Stream<Item = i32, Error = ParseIntError> {
stream::iter_ok(0..i)
}
fn convert_to_string(number: i32) -> Result<String, ParseIntError> {
Ok(number.to_string())
}
fn main() {
println!("start:");
let vec = into_many(10)
.map(|number| convert_to_string(number))
.collect()
.wait()
.unwrap();
println!("vec={:#?}", vec);
println!("finish:");
}
It outputs the following (i.e., Vec<Result<i32, ParseIntError>>):
start:
vec=[
Ok(
"0"
),
Ok(
"1"
),
Ok(
"2"
), ...
Is there any way to make it output a Vec<i32> and if any error happens than immediately stop execution and return from the function (e.g., like this example)?
Note: I do want to use use futures::Stream; // 0.1.25 even if it doesn't make sense for this particular example.
The following code (playground link) as a modification of your current code in your question gets the result you want:
use futures::{stream, Future, Stream}; // 0.1.25
use std::num::ParseIntError;
fn into_many(i: i32) -> impl Stream<Item = i32, Error = ParseIntError> {
stream::iter_ok(0..i)
}
fn convert_to_string(number: i32) -> Result<String, ParseIntError> {
Ok(number.to_string())
}
fn main() {
println!("start:");
let vec: Result<Vec<String>, ParseIntError> = into_many(10)
.map(|number| convert_to_string(number))
.collect()
.wait()
.unwrap()
.into_iter()
.collect();
println!("vec={:#?}", vec);
println!("finish:");
}
Since your current code returned a Vec, we can turn that into an iterator and collect that into the type you want. Type annotations are needed so that collect knows what type to collect the iterator into.
Note that the collect method on the Iterator trait isn't to be confused with the collect method on a Stream.
Finally, while this works, it may not be exactly what you want, since it still waits for all results from the stream to be collected into a vector, before using collect to transform the vector. I don't have experience with futures so not sure how possible this is (it probably is but may require a less neat functional programming style solution).
map with a function that returns Result
Don't do this, that's not when you should use map. Instead, use and_then:
let vec = into_many(10)
.and_then(|number| convert_to_string(number))
.collect()
.wait()
.unwrap();
You should practice with simpler Rust concepts like Option, Result, and iterators before diving into futures. Many concepts transfer over.
See also:
How do I unwrap an arbitrary number of nested Option types?
What is the idiomatic way to handle/unwrap nested Result types?