The reader() method works, and the data from the file is loaded, but the writer() does not work, and there is no error, it simply does not write silently. But when I open the file manually (without using the CookieStoreFS structure) and create a BufWriter, everything works fine.
I have following code:
use std::{path::PathBuf, env::current_dir, fs::File, error::Error, io::{BufReader, BufWriter}};
//TODO ERROR IF COOKIE FILE NOT EXISTS
pub struct CookieStoreFS{
path: PathBuf,
}
impl Default for CookieStoreFS {
fn default() -> Self {
let mut default_path = current_dir().unwrap();
default_path.push("cookie.json");
Self { path: default_path }
}
}
impl CookieStoreFS{
pub fn new(path: PathBuf) -> Self{
Self{
path
}
}
fn create(&self) -> Result<File, Box<dyn Error>>{
match File::create(&self.path){
Ok(f) => Ok(f),
Err(err) => Err(err.into())
}
}
fn open(&self) -> Result<File, Box<dyn Error>>{
if !self.path.exists(){
return self.create()
}
return match File::open(&self.path){
Ok(f) => Ok(f),
Err(err) => Err(err.into())
}
}
pub fn reader(&self) -> Result<BufReader<File>, Box<dyn Error>>{
let file = self.open()?;
Ok(BufReader::new(file))
}
pub fn writer(&self) -> Result<BufWriter<File>, Box<dyn Error>>{
let file = self.open()?;
Ok(BufWriter::new(file))
}
}
and it doesn't write to the file without any errors:
let mut writer = COOKIE_STORE_FS.writer().unwrap();
writeln!(writer, "asdasd");
writeln!(&mut writer, "asdasd");
I fixed my problem with following code:
File::options().read(true).write(true).open(&self.path)
Related
I am trying to figure out how the error enum works to wrap std::error:Error by passing in bad input for Url. I wish to wrap url::ParseError.
use std::error::Error;
use std::fmt::Display;
#[derive(Debug)]
pub enum WordCount {
UrlError(url::ParseError),
}
impl Display for WordCount {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WordCount::UrlError(e) => {
write!(f, "{}", e)
},
}
}
}
impl std::error::Error for WordCount {}
impl From<url::ParseError> for WordCount {
fn from(err: url::ParseError) -> Self {
WordCount::UrlError(err)
}
}
pub fn run() -> Result<String, WordCount> {
let args: Vec<String> = env::args().collect();
let arg = &args[1].as_ref();
let domain = Url::parse(arg);
match domain {
Ok(_) => {}
Err(err) => return Err(WordCount::UrlError(err)),
};
Ok("".to_owned())
}
If I replace the following line:
Err(err) => return Err(WordCount::UrlError(err)), in the fn run()
with: panic!("some string") e.g
match domain {
Ok(domain) => println!("happy domain"),
Err(err) => panic!("panicking"),
};
Then I can see an error print to the terminal. But not with the implementation, nothing prints to stderr even if I have implemented Display.
Just returning an error doesn't implicetly print it. You could make your main function to return the same error, then your program will report the message at the end of execution. As an example:
use url::Url;
use std::error::Error;
use std::fmt::Display;
#[derive(Debug)]
pub enum CrawlerError {
UrlError(url::ParseError),
}
impl Display for CrawlerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CrawlerError::UrlError(e) => {
write!(f, "{}", e)
},
}
}
}
impl std::error::Error for CrawlerError {}
impl From<url::ParseError> for CrawlerError {
fn from(err: url::ParseError) -> Self {
CrawlerError::UrlError(err)
}
}
pub fn run() -> Result<String, CrawlerError> {
let domain = Url::parse("foo/bar");
match domain {
Ok(_) => Ok("".to_string()),
Err(err) => Err(CrawlerError::UrlError(err)),
}
}
fn main() -> Result<(), CrawlerError> {
run()?;
Ok(())
}
Playground
Results:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 1.25s
Running `target/debug/playground`
Error: UrlError(RelativeUrlWithoutBase)
I'm not really sure how to write a high-order function that wants to return an error of its own and make it look nice when I use it on another functions that also return a Result.
This is hard to describe. Here's an example:
use std::fs;
use std::io;
use std::io::Read;
use std::fmt;
use std::error::Error;
#[derive(Debug)]
struct MyError;
impl Error for MyError {}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MyError")
}
}
fn log_something() -> Result<(), MyError> {
unimplemented!()
}
fn log_and_call<T>(f: &dyn Fn() -> T) -> Result<T, MyError> {
log_something()?;
Ok(f())
}
fn the_answer() -> i32 {
42
}
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = fs::File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main() {
let _logged_answer: Result<i32, MyError> = log_and_call(&the_answer);
let _logged_username: Result<Result<String, io::Error>, MyError> = log_and_call(&read_username_from_file);
}
How do I avoid having that Result<Result<String, io::Error>, MyError> there? I'd like to have Result<String, Error>, or maybe something even more sensible.
Maybe there's a more idomatic way to do this?
You can do this with the help of traits:
fn log_and_call<T: ToResult>(f: &dyn Fn() -> T) -> Result<T::Value, MyError> {
log_something()?;
f().to_result()
}
fn the_answer() -> i32 {
42
}
trait ToResult {
type Value;
fn to_result(self) -> Result<Self::Value, MyError>;
}
impl<T, E: Error> ToResult for Result<T, E> {
type Value = T;
fn to_result(self) -> Result<Self::Value, MyError> {
self.map_err(|err| MyError::from(err))
}
}
impl ToResult for i32 {
type Value = i32;
fn to_result(self) -> Result<Self::Value, MyError> {
Ok(self)
}
}
fn main() {
let _logged_answer: Result<i32, MyError> = log_and_call(&the_answer);
let _logged_username: Result<String, MyError> = log_and_call(&read_username_from_file);
}
Playground
I don't exactly know your use case, so if you just want to grab the value inside the result wherever you are calling, Box<dyn std::error::Error> can be used at return position:
fn main() -> Result<(), Box<dyn Error>> {
let _logged_answer: i32 = log_and_call(&the_answer)?;
let _logged_username: String = log_and_call(&read_username_from_file)??;
Ok(())
}
Playground
Another way could be to change log_and_call to this:
fn log_and_call<T, E: Error>(f: &dyn Fn() -> Result<T, E>) -> Result<T, MyError> where MyError: From<E> {
log_something()?;
Ok(f()?)
}
and then calling the function like:
fn main() {
let _logged_answer: Result<i32, MyError> = log_and_call(&||-> Result<_, MyError> {Ok(the_answer())});
let _logged_username: Result<String, MyError> = log_and_call(&read_username_from_file);
}
Playground
You could define a macro or a function to do this wrapping in a closure returning Result:
macro_rules! wrap {
($($content:tt)+) => {
||-> Result<_, MyError> {Ok($($content)+)}
};
}
fn main() {
let _logged_answer: Result<i32, MyError> = log_and_call(&wrap!(the_answer()));
let _logged_username: Result<String, MyError> = log_and_call(&read_username_from_file);
}
Playground
Another solution could be to have something like flatten_result as suggested by Aplet in question comments.
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 want to make a struct which the text output can be either displayed on the console or stored in an internal buffer. If the text is buffered, then I need a method that gives back the text content.
For this aim I use a property named writer, which is dyn std::io::Write (wrapped into Rc<RefCell<>> because it is needed by my real code). Then on struct construction, I create either a io::stdout() instance or a Vec::<u8>::new() instance for this property.
use std::rc::Rc;
use std::cell::RefCell;
use std::io;
struct A {
// Rc<RefCell<>> is needed in my real code
writer: Rc<RefCell<dyn io::Write>>,
}
impl A {
pub fn new() -> Self {
Self { writer: Rc::new(RefCell::new(io::stdout())) }
}
pub fn new_buffered() -> Self {
Self { writer: Rc::new(RefCell::new(Vec::<u8>::new())) }
}
pub fn write(&self, s: &str) {
let mut writer = self.writer.borrow_mut();
writeln!(writer, "{}", s).unwrap();
}
/// Returns None if the struct is not buffered, otherwise a copy of the buffered output.
pub fn get_buffer(&self) -> Option<String> {
match GET_VEC_U8() { // <- Unable to implement this line
Some(vec_u8) => {
Some(String::from_utf8(vec_u8.clone()).unwrap())
},
None => None,
}
}
}
fn main() {
let a = A::new();
a.write("foo");
println!("Buffer: {:?}", a.get_buffer());
let b = A::new_buffered();
b.write("bar");
println!("Buffer: {:?}", b.get_buffer());
}
Question
But I can't figure out how to extract the text content (method get_buffer()), when the writer is Vec<u8>. How can I do it ?
My try
I tried to wrap the property into a Box:
struct A {
writer: Rc<RefCell<Box<dyn io::Write>>>,
}
then use Box::downcast() on it:
impl A {
pub fn get_buffer(&self) -> Option<String> {
let writer = self.writer.borrow();
match (*writer).downcast::<Vec<u8>>() {
Ok(vec_u8) => Some(String::from_utf8(vec_u8.clone()).unwrap()),
Err(_) => None,
}
}
}
but I get this error:
error[E0599]: no method named `downcast` found for struct `std::boxed::Box<dyn std::io::Write>` in the current scope
--> src/main.rs:27:25
|
27 | match (*writer).downcast::<Vec<u8>>() {
| ^^^^^^^^ method not found in `std::boxed::Box<dyn std::io::Write>`
As #SvenMarnach wrote in the comments, writing a custom trait depending on io::Write can be a solution
use std::rc::Rc;
use std::cell::RefCell;
use std::io::{self, Stdout};
trait MyWrite: io::Write {
fn get_buffer(&self) -> Option<String>;
}
impl MyWrite for Stdout {
fn get_buffer(&self) -> Option<String> {
None
}
}
impl MyWrite for Vec<u8> {
fn get_buffer(&self) -> Option<String> {
Some(String::from_utf8(self.clone()).unwrap())
}
}
struct A {
// Rc<RefCell<>> is needed in my real code
writer: Rc<RefCell<dyn MyWrite>>,
}
impl A {
pub fn new() -> Self {
Self { writer: Rc::new(RefCell::new(io::stdout())) }
}
pub fn new_buffered() -> Self {
Self { writer: Rc::new(RefCell::new(Vec::<u8>::new())) }
}
pub fn write(&self, s: &str) {
let mut writer = self.writer.borrow_mut();
writeln!(writer, "{}", s).unwrap();
}
/// Returns None if the struct is not buffered, otherwise a copy of the buffered output.
pub fn get_buffer(&self) -> Option<String> {
let writer = self.writer.borrow();
writer.get_buffer()
}
}
fn main() {
let a = A::new();
a.write("foo");
println!("Buffer: {:?}", a.get_buffer());
let b = A::new_buffered();
b.write("bar");
println!("Buffer: {:?}", b.get_buffer());
}
I have a crate with the function sys_info::hostname which returns the hostname. The only problem is that hostname returns Result<String, Error>, but I need another function that must have a return type Result<(), String>. How can I call sys_info::hostname and return the hostname in a function that doesn't return the same type? Before you ask, the second function's return type cannot be changed due to formatting issues.
Instead of using the try! macro, which you can't use if the result type is incompatible, use a match statement to take apart the return value of sys_info::hostname and do what you need to with its parts. Example
struct Error;
fn thing_returning_result(succeed: bool) -> Result<String, Error> {
if succeed {
Ok("Hello".into())
} else {
Err(Error)
}
}
fn thing_returning_other_result(succeed: bool) -> Result<(), String> {
match thing_returning_result(succeed) {
Ok(s) => Err(s),
Err(_) => Err("whoopsies".into())
}
}
fn main() {
println!("{:?}", thing_returning_other_result(false));
}
I'd use map and map_err:
struct Error;
fn inner() -> Result<String, Error> {
Err(Error)
}
fn outer() -> Result<(), String> {
inner()
.map(|ok_val| ())
.map_err(|err_val| "Something".to_string())
}
fn main() {
outer();
}
Here's an example:
First try:
fn is_url(val: &str) -> Result<(), String> {
match Url::parse(val) {
Ok(_) => Ok(()),
Err(_) => Err(format!("The URL '{}' is an invalid URL.", val)),
}
}
More concise:
fn is_url(val: &str) -> Result<(), String> {
Url::parse(val).map_err(|_| format!("The URL '{}' is an invalid URL.", val))?;
Ok(())
}