given a std::time::SystemTime in the past, I'd like to manipulate elapsed() in a method via:
fn render(&self) -> Result<(), String> {
...
let elapsed = self.start.elapsed()?.as_secs();
...
}
however, ? operator wants to convert std::time::SystemTimeError to String, and From for std::time::SystemTimeError doesn't provide such a conversion. Unfortunately, it doesn't seem like you can:
impl From<std::time::SystemTimeError> for std::time::SystemTimeError {
fn from(e: std::time::SystemTimeError) -> Self { ... }
}
I really don't want to have to add match to handle this, nor do I simply want to unwrap() without error checking. I could define a fn that wraps the match and returns a Result<std::time::Duration, String>, but that seems misguided. What am I missing?
Update:
After much futzing around with snafu; yes, I really like it. Adding the SystemTimeError was a breeze. However, it took me a while to sort out how to deal with the errors returned from the other crated (where they are Result<(), String>. I finally found that I just needed to add a GenericError to my Error enum and then implement std::convert::From for Error to create the GenericError.
Most people (myself included) will advise against using String with Result, as you can lose Error information that way. However I don't think that's the whole story. For your example, you can use the venerable map_err:
let elapsed = self.start.elapsed().map_err(|e|
e.to_string()
)?;
as detailed by Andrew Gallant. If you want to include full Error object,
you can do something like this:
let elapsed = self.start.elapsed().map_err(|e|
format!("{:?}", e)
)?;
Or, depending on the situation you might be able to avoid both Results and
panics:
let elapsed = self.start.elapsed().unwrap_or_default();
Related
I have a function like this
fn get_html(address: &str) -> String {
let mut response = reqwest::blocking::get(
address,
);
response = response.unwrap_or_else(|_e| {String::from("")});
response = response.text().unwrap_or_else(|_e| {String::from("")});
return response
}
Where I'm checking for html content. I would like to return an empty String if any kind of an error occurs somewhere in this function.
I'm not sure how to deal with this because unwrap_or_else expecting Result not String.
The reqwest::blocking::get() function is returning a Result<Response>.
To obtain the html, you have to unwrap this result and call .text() method.
That will return a Result<String>, that you have to unwrap again.
In your code you assign a String::from("") when you unwrap the Result<Response>, and that is not right, because you have a Response when it is Ok and a String when it is an Err.
Instead you should match the result and return the String from the function:
fn get_html(address: &str) -> String {
let mut response = reqwest::blocking::get(
address,
);
match response {
Ok(response) => response.text().unwrap_or_else(|_e| String::from("")),
Err(_) => String::from(""),
}
}
In this code, you use unwrap_or_else() just on the .text() result.
While if you have an error on the response itself, you return a String from the function.
An idiomatic way to solve your issue would be to refactor slightly your code: because code in get_html could fail, it's natural that its signature reflects this, so instead of returning a String, it could return an Option<String>, leaving the caller decide on what to do in this case:
fn get_html(address: &str) -> Option<String> {
reqwest::blocking::get(address)
.ok()?
.text()
.ok()
}
See the playground.
This makes the code much more straightforward. However, you may really want the signature of the function to be -> String, not -> Option<String>. In this case, there are two solutions.
The first solution would be to use the experimental try block. I mention this solution, not because it's currently the most adequate, but because it will be one day (most likely).
fn get_html(address: &str) -> String {
let result: Option<_> = try {
reqwest::blocking::get(address)
.ok()?
.text()
.ok()?
};
result.unwrap_or_default()
}
See the playground.
Note that, as is, Rust is not able to figure out types on its own for the try block, so we have to help it, which makes this more verbose. This aspect will probably improve over time, when try blocks are stabilized.
Also note that, since the Default::default constructor of String produces an empty string, we can directly use .unwrap_or_default() instead of .unwrap_or(String::new()) or .unwrap_or_else(|| String::new()) (since an empty string is not allocated, the first option is also acceptable).
The second solution would simply be to add an other function with the wanted signature, and make it use the first function
fn get_html_failable(address: &str) -> Option<String> {
reqwest::blocking::get(address)
.ok()?
.text()
.ok()
}
fn get_html(address: &str) -> String {
get_html_failable(address).unwrap_or_default()
}
This may seem unconvenient, but lots of libraries in Rust have internal error propagation using types that represent failure (such as Result or Option), because it's really convenient to write such functions and to compose them (both reqwest::blocking::get and .text() return a Result, and see how easy it is to use them in a function that returns an Option), and have wrapper function that expose the wanted signature (most libraries will still expose fail types, but if you are writing an application it's not necessarily the best choice).
A variable of both preceding workarounds is to "simulate" a try block in stable Rust, without relying on an additional toplevel function.
fn get_html(address: &str) -> String {
(|| reqwest::blocking::get(address).ok()?.text().ok())()
.unwrap_or_default()
}
See the playground.
Note that, in this case, countrary to the try version, Rust is able to figure out the right types on its own.
This is probably the least readable solution, so I wouldn't recommend it (use try blocks instead). But it works.
TLDR: I want to replace a T behind &mut T with a new T that I construct from the old T
Note: please forgive me if the solution to this problem is easy to find. I did a lot of googling, but I am not sure how to word the problem correctly.
Sample code (playground):
struct T { s: String }
fn main() {
let ref mut t = T { s: "hello".to_string() };
*t = T {
s: t.s + " world"
}
}
This obviously fails because the add impl on String takes self by value, and therefore would require being able to move out of T, which is however not possible, since T is behind a reference.
From what I was able to find, the usual way to achieve this is to do something like
let old_t = std::mem::replace(t, T { s: Default::default() });
t.s = old_t + " world";
but this requires that it's possible and feasible to create some placeholder T until we can fill it with real data.
Fortunately, in my use-case I can create a placeholder T, but it's still not clear to me why is an api similar to this not possible:
map_in_place(t, |old_t: T| T { s: old_t.s + " world" });
Is there a reason that is not possible or commonly done?
Is there a reason [map_in_place] is not possible or commonly done?
A map_in_place is indeed possible:
// XXX unsound, don't use
pub fn map_in_place<T>(place: &mut T, f: impl FnOnce(T) -> T) {
let place = place as *mut T;
unsafe {
let val = std::ptr::read(place);
let new_val = f(val);
std::ptr::write(place, new_val);
}
}
But unfortunately it's not sound. If f() panics, *place will be dropped twice. First it will be dropped while unwinding the scope of f(), which thinks it owns the value it received. Then it will be dropped a second time by the owner of the value place is borrowed from, which never got the memo that the value it thinks it owns is actually garbage because it was already dropped. This can even be reproduced in the playground where a simple panic!() in the closure results in a double free.
For this reason an implementation of map_in_place would itself have to be marked unsafe, with a safety contract that f() not panic. But since pretty much anything in Rust can panic (e.g. any slice access), it would be hard to ensure that safety contract and the function would be somewhat of a footgun.
The replace_with crate does offer such functionality, with several recovery options in case of panic. Judging by the documentation, the authors are keenly aware of the panic issue, so if you really need that functionality, that might be a good place to get it from.
If I have a file like this:
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
Err("May
June")?
}
I get this result:
Error: "May\nJune"
Is it possible to get the unquoted string on output? Like this:
Error: May
June
I tried this:
Err("May
June").map_err(|e|
format!("{:?}", e)
)?
but it just made it worse:
Error: "\"May\\nJune\""
You have to print the error yourself instead of relying on the default fallback implementation.
main() -> Result<…> prints Debug version of the error (where strings are escaped). It's intended as a quick'n'dirty solution for examples or playpens, and not for real programs where anyone would care about presentation of the output.
Use:
fn main() {
if let Err(e) = run() {
eprintln!("{}", e);
std::process::exit(1);
}
}
fn run() -> Result<(), Box<dyn Error>> {
// etc
}
It will print the error using Display formatting.
There's nothing special about main()'s built-in error handling, so you're not losing anything by printing the error yourself.
There's also an alternative solution of implementing a custom Debug implementation on errors to make the Debug implementation print a non-debug one, but IMHO that's a hack, and needs more code than just doing the straightforward print. If you want that hack, have a look at the anyhow crate.
It might be overkill to pull in an extra dependency for this, but you can use the terminator crate, which offers a new error type intended to be returned from main that delegates to the Display implementation when printed with Debug. Then your example would look like this...
use terminator::Terminator;
fn main() -> Result<(), Terminator> {
Err("May
June")?
}
...and the output would be this:
Error: May
June
Result.expect()'s console output wasn't what I needed, so I extended Result with my own version:
trait ResultExt<T> {
fn or_exit(self, message: &str) -> T;
}
impl<T> ResultExt<T> for ::std::result::Result<T, Error> {
fn or_exit(self, message: &str) -> T {
if self.is_err() {
io::stderr().write(format!("FATAL: {} ({})\n", message, self.err().unwrap()).as_bytes()).unwrap();
process::exit(1);
}
return self.unwrap();
}
}
As I understand, Rust doesn't support varargs yet, so I have to use it like that, correct?
something().or_exit(&format!("Ah-ha! An error! {}", "blah"));
That's too verbose compared to either Java, Kotlin or C. What is the preferred way to solve this?
I don't think the API you suggested is particularly unergonomic. If maximum performance matters, it might make sense to put the error generation in a closure or provide an API for that too, so the String is only allocated when there is actually an error, which might be especially relevant when something is particularly expensive to format. (Like all the _else methods for std::result::Result.)
However, you might be able to make it more ergonomic by defining a macro which takes a result, a &str and format parameters. This could look like this for example: (This is based on #E_net4's comment)
macro_rules! or_exit {
($res:expr, $fmt:expr, $($arg:tt)+) => {
$res.unwrap_or_else(|e| {
let message = format!($fmt, $($arg)+);
eprintln!("FATAL: {} ({})\n", message, e);
process::exit(1)
})
};
}
fn main() {
let x: Result<i32, &'static str> = Err("dumb user, please replace");
let _ = or_exit!(x, "Ah-ha! An error! {}", "blahh");
}
Rust Playground
Note this might not yield the best error messages if users supply invalid arguments, I did not want to change your code too much, but if you decide to actually have the macro only be sugar and nothing else you should probably extend your API to take a closure instead of a string. You might want also to reconsider the naming of the macro.
In my short Rust experience I ran into this pattern several times, and I'm not sure if the way I solve it is actually adequate...
Let's assume I have some trait that looks like this:
trait Container {
type Item;
fn describe_container() -> String;
}
And some struct that implements this trait:
struct ImAContainerType;
struct ImAnItemType;
impl Container for ImAContainerType {
type Item = ImAnItemType;
fn describe_container() -> String { "some container that contains items".to_string() }
}
This may be a container that has a knowledge about type of items it contains, like in this example, or, as another example, request which knows what type of response should be returned, etc.
And now I find myself in a situation, when I need to implement a function that takes an item (associated type) and invokes a static function of the container (parent trait). This is the first naive attempt:
fn describe_item_container<C: Container>(item: C::Item) -> String {
C::describe_container()
}
This does not compile, because associated types are not injective, and Item can have several possible Containers, so this whole situation is ambiguous. I need to somehow provide the actual Container type, but without providing any container data. I may not have the container data itself at all when I invoke this function!
In search for a solution, I find the documentation for std::marker::PhantomData. It says:
PhantomData allows you to describe that a type acts as if it stores a value of type T, even though it does not.
This has to be the Rust's replacement for Haskell's Proxy type, right? Let's try to use it:
fn describe_item_container<C: Container>(container: PhantomData<C>, item: C::Item) -> String {
C::describe_container()
}
let s = describe_item_container(PhantomData::<PhantomData<ImAContainerType>>, ImAnItemType);
println!("{}", s);
Compiling... Error:
error[E0277]: the trait bound `std::marker::PhantomData<ImAContainerType>: Container` is not satisfied
I ask #rust-beginners and get a response: PhantomData is not meant to be used that way at all! Also I got an advice to simply make a backward associated type link from Item to Container. Something like this:
trait Item {
type C: Container;
}
fn describe_item_container<I: Item>(item: I) -> String {
I::C::describe_container()
}
It should work, but makes things much more complicated (especially for cases when item can be placed in different container kinds)...
After a lot more experimentation, I do the following change and everything compiles and works correctly:
let s = describe_item_container(PhantomData::<ImAContainerType>, ImAnItemType);
println!("{}", s);
The change is ::<PhantomData<ImAContainerType>> to ::<ImAContainerType>.
Playground example.
It works, but now I'm completely confused. Is this the correct way to use PhantomData? Why does it work at all? Is there some other, better way to provide type-only argument to a function in Rust?
EDIT: There is some oversimplification in my example, because in that particular case it would be easier to just invoke ImAContainerType::describe_container(). Here is some more complicated case, when the function actually does something with an Item, and still requires container type information.
If you want to pass a type argument to a function, you can just do it. You don't have to leave it out to be inferred.
This is how it looks for your second example (playground):
fn pack_item<C: Container>(item: C::Item) -> ItemPacket {
ItemPacket {
container_description: C::describe_container(),
_payload: item.get_payload(),
}
}
fn main() {
let s = pack_item::<ImAContainerType>(ImAnItemType);
println!("{}", s.container_description);
let s = pack_item::<ImAnotherContainerType>(ImAnItemType);
println!("{}", s.container_description);
}