I want to handle the error in response by match - rust

I am receiving a response through the server, and when I am working on the error using match, I get an error during operation, and I do not know how to solve this error. Please help
let host_clint = Client::new();
let response = host_clint
.post("https://xnbng--80ak6aa92e.com")
.form(&hashmap)
.send()?;
let respose_req = match response.json::<USER>(){
Ok(data) => data,
Err(err) => {
println!("Error while receiving response body: {}", err);
}
};
Error:
error[E0308]: `match` arms have incompatible types
--> src\main.rs:268:13
|
265 | let respose_req = match response.json::<USER>(){
| ----------------------------------- `match` arms have incompatible types
266 | Ok(data) => data,
| ---- this is found to be of type `USER`
267 | Err(err) => {
268 | println!("Error while receiving response body: {}", err);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `USER`, found `()`
|
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

If you use match to assign a value to a variable, you have to return a value of the same type in both match conditions. The Ok condition returns data which is of type user, but the Err condition does only print a line to stdout and does not return a value that can be assigned to response_req.
let respose_req = match response.json::<USER>(){
Ok(data) => data, // <-- Returns the value from 'data' of type USER
Err(err) => {
println!("Error while receiving response body: {}", err); // <-- Returns nothing, just prints a line to the stdout
}
};
So, you have to return some value of the type 'user' in every match condition. But I recommend wrapping it into an option like so:
let respose_req: Option<USER> = match response.json::<USER>(){
Ok(data) => Some(data),
Err(err) => {
println!("Error while receiving response body: {}", err);
None
}
};
Or you could just handle the error like so:
let response_req = response.json::<USER>();
if let Err(err) = response_req {
println!("Error while receiving response body: {}", err);
// here you have to prevent your code from further execution by using break or return.
}
// now you can just safely unwrap response_req whenever you use it because the error case is already handled.
response_req.unwrap().DO_SOME_STUFF()

Related

Mismatched types error while deserializing with pattern matching

I'm attempting to deserialize a csv value into a different type of struct based on a value passed into a function.
I am not understanding why I am receiving a mismatched types error. Why does it find struct CitiRec when it lives in a different match arm?
pub fn get_uncat_rec(path: &PathBuf, bank_type: BankType) -> Vec<UnCatRecord> {
let mut reader = csv::Reader::from_path(path).unwrap();
let mut uncat_rec: Vec<UnCatRecord> = Vec::new();
for record in reader.deserialize() {
match bank_type {
BankType::Citi => {
let rec_result: Result<CitiRec, csv::Error> = record;
match rec_result {
Ok(rec_result) => {
uncat_rec.push(rec_result.to_uncat_rec());
}
Err(err) => {
println!("Error received deserializing Citi Record: {}", err);
}
}
}
BankType::Kasaka => {
let rec_result: Result<KasakaRec, csv::Error> = record; <-- **error here**
match rec_result {
Ok(rec_result) => {
uncat_rec.push(rec_result.to_uncat_rec());
}
Err(err) => {
println!("Error received deserializing Kasaka Record: {}", err);
}
}
}
_ => {}
}
Here is the error that receive:
error[E0308]: mismatched types
--> src\normalizer.rs:26:57
|
26 | let rec_result: Result<KasakaRec, csv::Error> = record;
| ----------------------------- ^^^^^^ expected struct `KasakaRec`, found struct `CitiRec`
| |
| expected due to this
|
= note: expected enum `Result<KasakaRec, _>`
found enum `Result<CitiRec, _>`
The type cannot be conditional. record has one, and exactly one, type.
From the first match arm the compiler is concluding that you're deserializing into CitiRecs. But in the second it appears that you are deserializing into KasakaRecs, and this is a conflict.
A way to solve that is to have a separate loop for each arm, so we can deserialize to different types:
pub fn get_uncat_rec(path: &PathBuf, bank_type: BankType) -> Vec<UnCatRecord> {
let mut reader = csv::Reader::from_path(path).unwrap();
let mut uncat_rec: Vec<UnCatRecord> = Vec::new();
match bank_type {
BankType::Citi => {
for record in reader.deserialize() {
let rec_result: Result<CitiRec, csv::Error> = record;
match rec_result {
Ok(rec_result) => {
uncat_rec.push(rec_result.to_uncat_rec());
}
Err(err) => {
println!("Error received deserializing Citi Record: {}", err);
}
}
}
}
BankType::Kasaka => {
for record in reader.deserialize() {
let rec_result: Result<KasakaRec, csv::Error> = record;
match rec_result {
Ok(rec_result) => {
uncat_rec.push(rec_result.to_uncat_rec());
}
Err(err) => {
println!("Error received deserializing Kasaka Record: {}", err);
}
}
}
}
_ => {}
}
}

Why does match will not release the mutable borrow until end of it's expression?

I want to return client, In this function in any circumstances the code will not continue after match so rust should allow returning client.
pub async fn call_query2(mut client: Client<Compat<TcpStream>>, query:&str) -> Result<(Vec<tiberius::Row>,Client<Compat<TcpStream>>),(tiberius::error::Error,Client<Compat<TcpStream>>)> {
match client.query(query, &[]).await {
Ok(stream) =>{
match stream.into_first_result().await {
Ok(rows) => Ok((rows,client)),
Err(e) => Err((e,client))
}
},
Err(e) => Err((e,client))
}
}
but the compiler return this error message:
match client.query(query, &[]).await {
| ------------------------------
| |
| borrow of `client` occurs here
| a temporary with access to the borrow is created here ...
...
102 | Err(e) => Err((e,client))
| ^^^^^^ move out of `client` occurs here
103 | }
104 | }
| - ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Result<QueryStream<'_>, tiberius::error::Error>`
It seems that match will not release the mutable borrow until end of it's expression, because this code will work but I'm not able to return client:
pub async fn call_query(mut client: Client<Compat<TcpStream>>, query:&str) -> Result<(Vec<tiberius::Row>,Client<Compat<TcpStream>>),tiberius::error::Error> {
let stream = client.query(query, &[]).await?;
return Ok((stream.into_first_result().await?, client));
}
Any idea?
The main thing is that the temporary value that is created with the match expression is not dropped (through the Drop trait) until after the match-expression. In a sense you can think of the code being something like this:
pub async fn call_query2(mut client: Client<Compat<TcpStream>>, query:&str) -> Result<(Vec<tiberius::Row>,Client<Compat<TcpStream>>), (tiberius::error::Error,Client<Compat<TcpStream>>)> {
let result;
let tmp = client.query(query, &[]).await;
match tmp {
Ok(stream) => {
match stream.into_first_result().await {
Ok(rows) => result = Ok((rows,client)),
Err(e) => result = Err((e,client))
}
},
Err(e) => result = Err((e,client))
}
// tmp.drop(); happens here implicitly
return result;
}
Note that the implicit call tmp.drop() here theoretically might need access to client from the borrow checker's perspective.
Your other example works because you're basically dropping result before the return statement. Conceptually something like this:
pub async fn call_query(mut client: Client<Compat<TcpStream>>, query:&str) -> Result<(Vec<tiberius::Row>,Client<Compat<TcpStream>>),tiberius::error::Error> {
let result = client.query(query, &[]).await;
if let Err(e) = result {
return Err( e );
}
let stream = result.unwrap();
return Ok((stream.into_first_result().await?, client));
}
Note that you couldn't return an Err( (e,client) ) inside the if here either or you'd get again the same error from the borrow checker, since result hasn't been dropped yet.
That being said -- why would you want to return client in the first place? Probably because you want to use client in the calling code again. But then your function shouldn't require the caller to give up ownership to client in the first place. Just change mut client: ... in the signature of your function to client: &mut ... and remove Client from the return value type like this:
pub async fn call_query2(client: &mut Client<Compat<TcpStream>>, query:&str) -> Result<Vec<tiberius::Row>, tiberius::error::Error> {
// same code as before, but changing the result so that
// it doesn't return client anymore, i.e,
// Ok( (rows,client) ) => Ok(rows) and same for Err
}
Now your calling code can still refer to client without needing it "passed back" from you function. I.e., you go from
if let Ok( (rows, old_new_client) ) = call_query(client, query) {
old_new_client.whatever();
}
to a much nicer
if let Ok(rows) = call_query2(&mut client, query) {
client.whatever();
}

Rust: Closure composition ownership

To avoid nesting matches I am trying to use the and_then method for Result. The problem arises when I try to convert this
match Connection::open(&db_path) {
Ok(conn) => {
match conn.prepare("SELECT qwe,username_value,wer FROM my_table;") {
Ok(mut stmt) => {
match stmt.query_map([], |row| {Ok(row.get_unwrap::<usize, String>(1).clone())}) {
Ok(usernames) => {
for username in usernames {
if username.is_ok() {
println!("{}", username.expect("Could not read username value"));
}
}
println!("Query done");
},
Err(err) => println!("Error {}", err),
}
},
Err(err) => println!("Error {}", err),
}
},
Err(err) => println!("Error {}", err),
}
into this
match Connection::open(&db_path)
.and_then(move |conn: Connection| conn.prepare("SELECT qwe,username_value,wer FROM my_table;"))
.and_then(|mut stmt: Statement| stmt.query_map([], |row| {Ok(row.get_unwrap::<usize, String>(1).clone())})) {
Ok(usernames) => {
for username in usernames {
if username.is_ok() {
println!("{}", username.expect("Could not read username value"));
}
}
println!("Query done");
},
Err(_) => println!("Error querying db"),
}
The SQL lib is rusqlite.
The first snippet is accepted by the compiler, the second one is not.
The compiler screams at me with the following error message:
error[E0515]: cannot return value referencing function parameter `conn`
--> src\main.rs:134:43
|
134 | .and_then(move |conn: Connection| conn.prepare("SELECT qwe,username_value,wer FROM my_table;"))
| ----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `conn` is borrowed here
error[E0515]: cannot return value referencing function parameter `stmt`
--> src\main.rs:135:41
|
135 | .and_then(|mut stmt: Statement| stmt.query_map([], |row| {Ok(row.get_unwrap::<usize, String>(1).clone())})) {
| ----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `stmt` is borrowed here
error: aborting due to 2 previous errors
I tried with move on the three closures but the error message is the same.
Why is the ownership wrong in the second snippet and which is the idiomatic way of writing something like this?
The problem is that conn.prepare returns a statement that refers to conn, but conn is destroyed at the end of the closure so it's no longer here when the statement tries to use it. The idiomatic way to do what you want would be to use the ? operator:
let conn = Connection::open(&db_path)?;
let mut stmt = conn.prepare("SELECT qwe,username_value,wer FROM my_table;")?;
for username in stmt.query_map([], |row| {Ok(row.get_unwrap::<usize, String>(1).clone())})? {
if username.is_ok() {
println!("{}", username.expect("Could not read username value"));
}
}
And let the caller handle the error. If you want to handle the error yourself, you can wrap the above code in a local function or closure (see also this question).

Match statement using moved matched variable throwing compiler error

I am attempting to iterate through a Vec<String> of file paths and read the content of the files. However, the match statement throws a compiler error regarding the variable in the pattern. The following is the code:
//paths: Vec<String>
//get counts in paths
for path in paths.iter() {
let mut path_result: Result<std::fs::File, std::io::Error>;
let file: std::fs::File;
if path != "-" {
path_result = std::fs::File::open(&path);
}
//match result with error
match path_result {
Ok(path_file) => { file = path_file },
Err(e) => {
match e.kind() {
std::io::ErrorKind::NotFound => { println!("{}: No such file or directory", &path) },
std::io::ErrorKind::PermissionDenied => { println!("{}: Permission denied", &path) },
_ => {
println!("{}: Unknown error", &path);
panic!("unknown error opening file");
}
}
continue;
}
};
/*get content from file variable*/
}
This is the compiler error I receive:
error[E0382]: use of moved value
--> src/main.rs:60:16
|
51 | let mut path_result: Result<std::fs::File, std::io::Error>;
| --------------- move occurs because `path_result` has type `std::result::Result<File, std::io::Error>`, which does not implement the `Copy` trait
...
60 | Ok(path_file) => { file = path_file },
| ^^^^^^^^^ value used here after move
...
75 | }
| -
| |
| value moved here, in previous iteration of loop
| value moved here, in previous iteration of loop
error: aborting due to previous error; 16 warnings emitted
For more information about this error, try `rustc --explain E0382`.
error: could not compile `test`
To learn more, run the command again with --verbose.
The warnings are in regards to unused variables not included in this snippet.
I have attempted borrowing the path_file variable's contents, but receive the same error. My understanding of the situation is that, because neither path_file nor path_result are used later in the for block and path_result is rebinded at the start, there should be no issue even if the ownership goes out of scope past the match statement. However, that seems to not be the case.
You cannot use uninitialized values in Rust. This code does not work:
// declare path_result here
let mut path_result: Result<std::fs::File, std::io::Error>;
// conditionally initialize it here
if path != "-" {
path_result = std::fs::File::open(&path);
}
// potentially use uninitialized value: error!
match path_result {
The code above is also why you're getting the value moved here, in previous iteration of the loop error, because you're not actually initializing path_result in every iteration of the loop. Once you refactor the code to unconditionally initialize path_result in every iteration then it compiles:
fn example(paths: Vec<String>) {
for path in paths.iter() {
if path == "-" {
println!("skipping un-openable path: -");
continue;
}
let file = match std::fs::File::open(&path) {
Ok(path_file) => path_file,
Err(e) => {
match e.kind() {
std::io::ErrorKind::NotFound => {
println!("{}: No such file or directory", &path)
}
std::io::ErrorKind::PermissionDenied => {
println!("{}: Permission denied", &path)
}
_ => {
println!("{}: Unknown error", &path);
panic!("unknown error opening file");
}
}
continue;
}
};
/* get content from file variable */
}
}
playground

nickel.rs post redirect not found

I am using nickel.rs, PostgreSQL, and Angular.js. I can insert into my table with an HTTP POST:
// insert
{
let conn = shared_connection.clone();
router.post("/api/movies", middleware! { |request, mut response|
let conn = conn.lock().unwrap();
let stmt = match conn.prepare("insert into movie (title, releaseYear, director, genre)
values ($1, $2, $3, $4)") {
Ok(stmt) => stmt,
Err(e) => {
return response.send(format!("Preparing query failed: {}", e));
}
};
let movie = request.json_as::<MovieInsert>().unwrap();
match stmt.execute(&[
&movie.title.to_string(),
&movie.releaseYear,
&movie.director.to_string(),
&movie.genre.to_string()
]) {
Ok(v) => println!("Inserting movie was Success."),
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
// ERROR (1)
// return response.set(Location("/".into()));
});
}
I know this works fine because the row is inserted in the PostgreSQL table. However, the Chrome web browser shows an error:
POST http://localhost:6767/api/movies 404 (Not Found)
I also added the code in ERROR (1) line
response.set(Location("/".into()));
however, console show the error.
expected `core::result::Result<nickel::middleware::Action<nickel::response::Response<'mw, _>, nickel::response::Response<'mw, _, hyper::net::Streaming>>, nickel::nickel_error::NickelError<'mw, _>>`,
found `&mut nickel::response::Response<'_, _>`
(expected enum `core::result::Result`,
found &-ptr)
Now it is my code applied what Shepmaster said.
// insert
{
let conn = shared_connection.clone();
router.post("/api/movies", middleware! { |request, mut response|
let conn = conn.lock().unwrap();
let stmt = match conn.prepare("insert into movie (title, releaseYear, director, genre)
values ($1, $2, $3, $4)") {
Ok(stmt) => stmt,
Err(e) => {
return response.send(format!("Preparing query failed: {}", e));
}
};
let movie = request.json_as::<MovieInsert>().unwrap();
match stmt.execute(&[
&movie.title.to_string(),
&movie.releaseYear,
&movie.director.to_string(),
&movie.genre.to_string()
]) {
Ok(v) => println!("Inserting movie was Success."),
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
response.set(StatusCode::PermanentRedirect)
.set(Location("/".into()));
""
});
}
but the error occurred.
src/main.rs:155:18: 155:43 error: the trait modifier::Modifier<nickel::response::Response<'_, _>> is not implemented for the type hyper::header::common::location::Location [E0277]
src/main.rs:155 .set(Location("/".into()));
finally I fix like this!
Ok(v) => {
println!("Inserting movie was Success.");
response.set(StatusCode::Ok);
},
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
//response.set(StatusCode::PermanentRedirect)
// .set(Location("/".into()));
//""
return response.send("");
The code, as currently listed, doesn't make any sense. There's nothing returned from your route handler:
match ... {
Ok(v) => println!("Inserting movie was Success."),
Err(e) => println!("Inserting movie failed. => {:?}", e),
};
Because nothing about the database or the frontend framework is important, your code is equivalent to this:
#[macro_use]
extern crate nickel;
use nickel::{Nickel, HttpRouter};
fn main() {
let mut server = Nickel::new();
server.post("/api/movies", middleware! { |request, mut response|
println!("Hello");
});
server.listen("127.0.0.1:6767");
}
If you return something from the handler, then the HTTP status code changes from a 404 to a 200.
If you'd like to redirect somewhere, you need to explicitly change the status code. The documentation for Response::set happens to have an example:
server.get("/a", middleware! { |_, mut res|
res.set(StatusCode::PermanentRedirect)
.set(Location("http://nickel.rs".into()));
""
});
Note that an empty string is returned, similar to before. You were trying to return the Response type directly.

Resources