I have a function used to log errors
trait LogTrait {
fn log(self, mess: &str, file: &str, line: u32) -> Self;
}
impl<T, E> LogTraitfor Result<T, E>
where
E: std::fmt::Display,
{
fn log(self, mess: &str, file: &str, line: u32) -> Self {
if let Err(err) = &self {
fs::write("errors.log", format!("{:1} :F{:2}L{:3}",
mess, file, line.to_string().as_str())
);
}
self
}
}
Used this way
SomeFunction.log("my message", file!(), line!())?
In C++, I would create a macro to only fill in my message, but keeping calling file!() and line!() where the macro is called:
#define LOG(MESSAGE) log(MESSAGE, file!(), line!())
I know Rust's macros cannot be used for text substitution. But what is the proper way to achieve that in Rust, then?
Related
I would like to print T if Ok(T) or a generic message and do this as part of the same println!() statement.
My Current solution which works is:
fn main() {
let x: std::io::Result<i32> = Ok(54);
println!("hello {} ", x.map(|i| i.to_string()).unwrap_or("Bad".to_string()));
}
Is there a simpler and more efficient way that would leverage the Display trait instead of needing to convert to string inside map and unwrap_or?
You can cast each of the Result values as &dyn Display
let display = match &x {
Ok(i) => i as &dyn Display,
Err(_) => &"error" as &dyn Display,
};
// or with map
let display = x
.as_ref()
.map(|x| x as &dyn Display)
.unwrap_or_else(|_| &"error" as &dyn Display);
println!(
"hello {} ",
display
)
Both can be in-lined into the println!, I have just separated them for readability.
While the accepted solution works and answers your question exactly, I don't think it's the most readable (or performant). (Correction: it's in fact faster.) A trait object feels like overkill to me. Consider using the same approach as yours, but instead, merge the map() and unwrap_or() into map_or() (or map_or_else(), to avoid an unnecessary heap allocation).
use std::io;
fn main() {
let x: io::Result<i32> = Ok(54);
println!("Hello, {}.", x.map_or_else(|_err| "Bad".into(), |i| i.to_string()));
}
Run this snippet on Rust Playground.
If you're not going to use the error value, you can make the code a touch more concise by renaming _err to _.
On the other hand, if you're going to be doing this often and in many places, you can create a wrapper type and implement your Display logic for that.
use std::fmt::{self, Display};
use std::io;
fn main() {
let x: io::Result<i32> = Ok(54);
println!("Hello, {}.", PrintableResult(&x));
}
struct PrintableResult<'a, T: Display, E>(&'a Result<T, E>);
impl<'a, T: Display, E> Display for PrintableResult<'a, T, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
self.0
.as_ref()
.map_or_else(|_| "Bad".into(), |val| val.to_string())
)
}
}
Run this snippet on Rust Playground.
If you don't mind PrintableResult taking ownership of your Result value, you can drop the borrow and the lifetime specifier with it.
If you'd like, you can also implement a to_printable() extension method for Result (via a trait).
// ...
fn main() {
let x: io::Result<i32> = Ok(54);
println!("Hello, {}.", x.to_printable());
let y: Result<i32, &'static str> = Err("Something went wrong.");
println!("Hello, {}.", y.to_printable());
}
// ...
trait ToPrintable<T: Display, E> {
fn to_printable(&self) -> PrintableResult<T, E>;
}
impl<T: Display, E> ToPrintable<T, E> for Result<T, E> {
fn to_printable(&self) -> PrintableResult<T, E> {
PrintableResult(&self)
}
}
Run this snippet on Rust Playground.
I would like to have a main function that can fail, return Err(String) and print it in case of failure.
I tried it like this:
fn main() -> Result<(), String> {
Err(String::from("Line1\nLine2"))
}
It works well with simple strings, but for multiline errors \n or \r\n is displayed instead of a line break.
How is it possible to correctly display line breaks?
Not with the builtin printing, as it always uses Debug formatting. But you can print your own. You can even have a little wrapper that forwards Debug to Display, having a convienet error handling (but it may break with types that are not the error type themselves but are convertible into it, which will work implicitly with the error type operator but requires an explicit conversion with PrettyError. However, if you keep PrettyError only in main() it should be good enough):
use std::fmt;
pub struct PrettyError<T>(pub T);
impl<T> From<T> for PrettyError<T> {
fn from(v: T) -> Self {
Self(v)
}
}
impl<T: fmt::Display> fmt::Debug for PrettyError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
fn main() -> Result<(), PrettyError<String>> {
Err(String::from("Line1\nLine2"))?;
Ok(())
}
// Or
fn main() -> Result<(), PrettyError<String>> {
Err(PrettyError(String::from("Line1\nLine2")))
}
Suppose I have a struct containing an Option<Resource>, where Resource is some type I need to work with that needs to allocate external resources (in the actual case, GPU memory) and so might fail. From within a method, I want to attempt the allocation if it hasn't been done already, but propagate or handle the error if it does fail.
If it weren't for the failure case, Option::get_or_insert_with would be perfect. As it is, the tidiest solution I have thought of involves an unwrap(), which is inelegant since it looks like a potential panic:
struct Container {
resource: Option<Resource>,
...
}
impl Container {
...
fn activate(&mut self) -> Result<(), Error> {
if self.resource.is_none() {
self.resource = Some(Resource::new()?);
}
let resource: &mut Resource = self.resource.as_mut().unwrap();
// ... now do things with `resource` ...
Ok(())
}
...
}
Is there a way to initialize the Option with less fuss than this? To be clear, I'm not solely looking for avoiding unwrap(), but also overall readability. If an alternative is much more complex and indirect, I'd rather stick with this.
Complete example code (on Rust Playground):
#[derive(Debug)]
struct Resource {}
#[derive(Debug)]
struct Error;
impl Resource {
fn new() -> Result<Self, Error> {
Ok(Resource {})
}
fn write(&mut self) {}
}
#[derive(Debug)]
struct Container {
resource: Option<Resource>,
}
impl Container {
fn new() -> Self {
Self { resource: None }
}
fn activate(&mut self) -> Result<(), Error> {
if self.resource.is_none() {
self.resource = Some(Resource::new()?);
}
self.resource.as_mut().unwrap().write();
Ok(())
}
}
fn main() {
Container::new().activate();
}
Indeed, get_or_insert_with() but returning Result<&mut T, E> is what you could use to simplify your code. However, as you already discovered Option doesn't have a e.g. try_get_or_insert_with() method.
Other workarounds would be similarly verbose. However, you could use a match and get_or_insert(), to avoid the unwrap() like this:
fn activate(&mut self) -> Result<(), Error> {
let res = match &mut self.resource {
Some(res) => res,
None => self.resource.get_or_insert(Resource::new()?),
};
res.write();
Ok(())
}
If this is used frequently, then you could also define your own try_get_or_insert_with() trait method, and implement it for Option<T>.
trait TryGetOrInsert<T> {
fn try_get_or_insert_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>;
}
impl<T> TryGetOrInsert<T> for Option<T> {
fn try_get_or_insert_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
where
F: FnOnce() -> Result<T, E>,
{
match self {
Some(value) => Ok(value),
None => Ok(self.get_or_insert(f()?)),
}
}
}
Then now you can simplify your activate() method, to the following:
fn activate(&mut self) -> Result<(), Error> {
let res = self.resource.try_get_or_insert_with(|| Resource::new())?;
res.write();
Ok(())
}
I'm trying to implement an abstraction that allows me to read from either a directory or a zip file. I start by implementing something of this sort:
pub trait FileOpener<'a> {
type ReaderType: Read;
fn open(&'a self, file_name: &str) -> Result<Self::ReaderType, Box<dyn Error>>;
}
pub struct DirectoryFileOpener<'a> {
root: &'a Path
}
impl<'a> DirectoryFileOpener<'a> {
pub fn new(root: &'a Path) -> Self {
DirectoryFileOpener { root }
}
}
impl<'a> FileOpener<'a> for DirectoryFileOpener<'a> {
type ReaderType = File;
fn open(&'a self, file_name: &str) -> Result<File, Box<dyn Error>> {
Ok(File::open(self.root.join(file_name))?)
}
}
But then I realize that the zip-rs package's zip::ZipFile is constructed from a mutable reference to the zip::ZipArchive which it is located in, so I end up with the following code:
use std::path::Path;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use zip::{ZipArchive, read::ZipFile};
use std::marker::PhantomData;
pub trait FileOpener<'a> {
type ReaderType: Read;
fn open(&'a mut self, file_name: &str) -> Result<Self::ReaderType, Box<dyn Error>>;
}
pub struct DirectoryFileOpener<'a> {
root: &'a Path
}
impl<'a> DirectoryFileOpener<'a> {
pub fn new(root: &'a Path) -> Self {
DirectoryFileOpener { root }
}
}
impl<'a> FileOpener<'a> for DirectoryFileOpener<'a> {
type ReaderType = File;
fn open(&'a mut self, file_name: &str) -> Result<File, Box<dyn Error>> {
Ok(File::open(self.root.join(file_name))?)
}
}
pub struct ZipFileOpener<'a, R: Read + Seek> {
zip: ZipArchive<R>,
phantom: PhantomData<&'a Self>
}
impl<'a, R: Read + Seek> ZipFileOpener<'a, R> {
pub fn new(zip: ZipArchive<R>) -> Self {
ZipFileOpener { zip, phantom: PhantomData }
}
}
impl<'a, R: Read + Seek> FileOpener<'a> for ZipFileOpener<'a, R> {
type ReaderType = ZipFile<'a>;
fn open(&'a mut self, file_name: &str) -> Result<ZipFile<'a>, Box<dyn Error>> {
Ok(self.zip.by_name(file_name)?)
}
}
I'm not sure if that's the most optimal way to write that, but at least it compiles. Then I try to use it as such:
fn load(root: &Path) -> Result<...> {
let mut opener = io::DirectoryFileOpener::new(root);
let a = Self::parse_a(opener.open("a.txt")?)?;
let b = Self::parse_b(opener.open("b.txt")?, a)?;
}
and I get cannot borrow 'opener' as mutable more than once at a time. This does not surprise me much, as I indeed use open(), which borrows opener as mutable, twice - although a is only a u64, and from my point of view it is unrelated to the lifetime of opener.open(), from the compiler's point of view it has to be in the same lifetime of the line below it, and thus we attempt to borrow opener as mutable twice.
However, I then look at the following code, which compiles and works well and which I started this whole thing by trying to improve:
fn load_zip(root: &Path) -> Result<...> {
let file = File::open(root)?;
let mut zip = ZipArchive::new(file)?;
let a = Self::parse_a(zip.by_name("a.txt")?)?;
let b = Self::parse_b(zip.by_name("b.txt")?, a)?;
}
This throws me off completely, because the function by_name() also borrows zip as mutable, and is also called twice! Why is it allowed to borrow zip as mutable twice here but not in the previous case?
After researching the issue and Rust's semantics deeper, and building on top of the notes by trentcl, I came to realize that the problem essentially boils down to defining the FileOpener trait where the lifetime argument is bound to the associated type and not to the trait itself, e.g.
pub trait FileOpener {
type ReaderType: Read;
fn open(&'a mut self, file_name: &str) -> Result<Self::ReaderType, Box<dyn Error>>;
}
impl<'a, R: Read + Seek> FileOpener for ZipFileOpener<R> {
type ReaderType = ZipFile<'a>;
...
}
However, this is known as generic associated types (GAT), and is not yet supported in Rust. The GAT RFC does however mention that in some cases the problem can be circumvented by binding the lifetime to the trait itself and using higher-rank trait bounds (HRTB) in the receiving function, which yields the following working solution to this question:
pub trait FileOpener<'a> {
type ReaderType: Read;
fn open(&'a self, file_name: &str) -> Result<Self::ReaderType, Box<dyn Error>>;
}
...
fn load<T: for<'a> FileOpener<'a>>(opener: T) -> ... {
let a = parse_a(opener.open("a.txt")?)?;
let b = parse_b(opener.open("b.txt")?, a)?;
}
This is because the HRTB allows us to bind T to a FileOpener without binding a specific lifetime to it, which enables the late binding of different lifetimes for each call to opener.open()
I have some functions that will return a different error type when failing.
First I have a builder, which contains this method:
#[derive(Debug)]
pub enum BuilderError {
ElementMissing(&'static str),
}
pub fn spawn(self) -> Result<ServiceStatus, BuilderError>
So it will return a BuildError on failure.
Now, I have another function that will return another error:
#[derive(Debug)]
pub enum XmlError {
XmlCreationFailed(writer::Error),
ConversionToUtf8(FromUtf8Error),
}
pub fn create_xml(service_status: super::ServiceStatus) -> Result<String, XmlError>
The idea is that I use the builder to create a ServiceStatus object and use it to create a XML string with create_xml function.
To do that, I have this code:
#[derive(Debug)]
pub enum WebserviceError {
XmlError(XmlError),
BuilderError(BuilderError),
}
impl std::error::Error for WebserviceError {
...
}
impl From<XmlError> for WebserviceError {
fn from(error: XmlError) -> WebserviceError {
WebserviceError::XmlError(error)
}
}
impl From<BuilderError> for WebserviceError {
fn from(error: BuilderError) -> WebserviceError {
WebserviceError::BuilderError(error)
}
}
fn test() -> Result<String, status::WebserviceError> {
...
let service_status = builder.spawn()?;
let xml = status::create_xml(service_status)?;
Ok(xml)
}
Now, I think I can do better using and_then instead of using ? operator:
fn test() -> Result<String, status::WebserviceError> {
...
builder
.spawn()
.map_err(status::WebserviceError::BuilderError)
.and_then(|hue| status::create_xml(hue).map_err(status::WebserviceError::XmlError))
}
This solution works too, but now I need to explicitly call map_err to convert from a BuilderError or XmlError to a WebserviceError...
So, my question is, can I do better? I think a solution like this would be ideal:
fn test() -> Result<String, status::WebserviceError> {
...
builder
.spawn()
.and_then(status::create_xml)
}
After some trial, here is the solution:
trait CustomAndThen<T, E> {
fn and_then2<U, E2, F: FnOnce(T) -> Result<U, E2>>(self, op: F) -> Result<U, E>
where E: std::convert::From<E2>;
}
impl<T, E> CustomAndThen<T, E> for Result<T, E> {
fn and_then2<U, E2, F: FnOnce(T) -> Result<U, E2>>(self, op: F) -> Result<U, E>
where E: std::convert::From<E2>
{
match self {
Ok(t) => op(t).map_err(From::from),
Err(e) => Err(e),
}
}
}
...
Ok(builder)
.and_then2(status::ServiceStatusBuilder::spawn)
.and_then2(status::create_xml)
This will create a custom and_then function for Result type that will make the conversion inside it, clearing the code
If you're not really interested in exact error, but raise some final error in the end you can use something like:
builder.spawn().ok()
.and_then(|v| status.create_xml(v).ok())
.ok_or_else(|| SomeError('failed to create xml'))