Cannot print error message from bmp::open - rust

I'm trying to print out the error it throws when bmp::open failed
let image_result = bmp::open(argument);
let image = match image_result {
Ok(i) => i,
Err(error) => {
println!("Error! {error:?}");
},
};
However, I've been getting the below error message
Thanks in advance!

This is because in Rust, the match statement has to return the same type from its match arms. So your first match arm
Ok(i) => i
returns a type of Image where as second match arm
Err(error) => {
println!("Error! {error:?}");
}
does not return anything hence the compiler infer the return type as ()(unit) type.
There are multiple ways you could solve this, but it's really depends upon how you want to handle with the error case. If your intention is to handle only Ok case, you could destructure the Result.
if let Ok(i) = bmp::open(argument) {
print("Do something with {i}")
}
Alternatively you can panic if the file is failed to open.
let image_result = bmp::open(argument);
let image = match image_result {
Ok(i) => i,
Err(error) => {
panic!("Error! {error:?}");
}
};
OR with unwrap_or_else,
let img = bmp::open("test/rgbw.bmp").unwrap_or_else(|e| {
panic!("Failed to open: {}", e);
});

Related

How call function on Err arm

I wrote this code and it works. But I have a custom class for outputting logs to stdout. I want to call my logging function if I get an Error instead of panic! macro.
fn create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(error) => {
panic!("There was a problem creating the file: {:?}", error)
}
};
logfile
}
For example I want get something like that:
let logfile = match File::create(filename) {
Ok(file) => file,
Err(e) => {
Log::error("Log file creation failed, reason: {}", e);
process::exit(1)
}
};
But compiler says:
[E0308] `match` arms have incompatible types.
[Note] expected struct `File`, found `()
How can I solve this problem?
If I put the error data to stderr will it help?
Your revised example with std::process::exit() works for me: (link)
use chrono; // 0.4.23
use std::fs::File;
use log; // 0.4.17
struct Log { }
impl Log {
fn format_date(date: chrono::DateTime<chrono::offset::Local>) -> i64 {
return 0;
}
}
fn old_create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(error) => {
panic!("There was a problem creating the file: {:?}", error)
}
};
logfile
}
fn new_create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(e) => {
// Instead of using `Log::error`, we'll use `log::error!` for show.
log::error!("Log file creation failed, reason: {}", e);
// This compiles.
std::process::exit(1)
}
};
logfile
}
fn main() {
new_create_logfile();
}
Normally, you need to make sure the return types of all match arms have the same type -- here, you are returning std::fs::File under the Ok branch, so the Err branch can "escape" the requirement by returning ! (pronounced "never") (link).
Since computation never returns from the std::process::exit() and its return type is marked as !, it's passes the type-checking stage.

Is there an alternative expression for match in Rust?

I know that when handling errors using Result and Option, it can be expressed more concisely by using unwrap_or_else, unwrap_or_default, etc. instead of match.
The following is an example of expressing the match expression more concisely using unwrap.
let engine_name = match config.engine_name {
Some(name) => name,
None => host_name.clone(),
};
->
let engine_name = config.engine_name
.unwrap_or_else(|| host_name.clone());
let group_name = match config.group_name {
Some(name) => name,
None => String::from("")
};
->
let group_name = config.group_name.unwrap_or_default();
Questions
Is there a function I can use instead of match if I want to put a return statement instead of putting a different value when an error occurs?
let accept_service = ServiceBuilder::new().service(match AcceptService::new() {
Ok(service) => service,
Err(e) => return Err(format!("failed to bind server socket: {}", e).into()),
});
You can use a combination of map_err() and the error propagation operator ?:
ServiceBuilder::new()
.service(AcceptService::new().map_err(|e| format!("failed to bind server socket: {}", e)?));

How to express variable type in match arm?

I'm trying to parse a piece of json string using serde_json in Rust. I want to match the result of the parse using the following syntax:
match serde_json::from_str(msg.to_text().unwrap()) {
Ok(result) => {
println!("Parsed: {}", response.text);
}
Err(error) => {
println!("Failed to parse: {}", error);
}
}
but the compiler complains to me that he doesn't know the type of the result and of course, he is right. But how can I tell him the type of the result? I tried the following code, but it didn't work either. So I want to express the type of variable in the match arm.
match serde_json::from_str(msg.to_text().unwrap()) {
Ok(result: Response) => {
println!("Parsed: {}", response.text);
}
Err(error) => {
println!("Failed to parse: {}, {}", error, msg.to_text.unwrap());
}
}
There's a few way you can specify the type. Either you can explicitly specify it using type parameters in the function call, i.e. by using this syntax func::<T>().
match serde_json::from_str::<Response>(json) {
Ok(response) => {}
Err(err) => {}
}
Alternatively, you can assign the initial result to a variable, and hint the type there, i.e.
let res: Result<Response, _> = serde_json::from_str(json);
match res {
Ok(response) => {}
Err(err) => {}
}
or
match serde_json::from_str(json) {
Ok(response) => {
let response: Response = response;
}
Err(err) => {}
}
Lastly you can also use # bindings. However, this doesn't work if your type is an enum, as then you'd have to specify the exact variant you wanted.
As in, if Response was an enum you could have multiple Ok(resp # Response::???) match arms. But you couldn't have a single Ok(resp # Response) match arm.
match serde_json::from_str(json) {
Ok(response # Response { .. }) => {}
Err(err) => {}
}

How can I return the matched value from env::var?

If the environment variable SOMEVALUE is found, it should be bound to k:
let k = match env::var(SOMEVALUE) {
Ok(val) => {return val},
Err(e) => println!("could not find {}: {}", SOMEVALUE, e),
};
Compiling this code gives the following error
|
11 | Ok(val) => {return val},
| ^^^ expected (), found struct `std::string::String`
|
= note: expected type `()`
found type `std::string::String`
What Victor wrote is correct, but you probably want to actually bind a value to k; in that case you should change your code to:
let k = match env::var(SOMEVALUE) {
Ok(val) => val,
Err(e) => panic!("could not find {}: {}", SOMEVALUE, e),
};
Equivalently, you can use Result::unwrap_or_else:
let k = env::var(SOMEVALUE).unwrap_or_else(|e| {
panic!("could not find {}: {}", SOMEVALUE, e)
});
In Rust, everything is an expression, there are only a few statements. Each expression returns a value and if the expression does not return anything explicitly, it returns a () (unit) value implicitly.
Your match expression returns different types from 2 branches - a std::String from Ok branch and () from Err branch.
To fix this, you must return something from the second branch too:
let k = match env::var(SOMEVALUE) {
Ok(val) => {return val},
Err(e) => {
println!("could not find {}: {}", SOMEVALUE, e);
String::default()
},
};
This returns a value of the same type - std::String.
Another option you can do is following (This just shows how we can use env::var_os to achieve same purpose)
let spdk_dir = match env::var_os("SPDK_DIR") {
Some(val) => val.into_string().unwrap(),
None => panic!("SPDK_DIR is not defined in the environment")
};
Here, we try to read the environment variable SPDK_DIR and if it is not defined, we exit the program.

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