Problem with OpenOptions and write_all()

Hi, I'm new to Rust :slightly_smiling_face:

I am writing a program that can log time & date into an existing log file, without replacing the old records inside but appending new records each time the program runs.

When I searched and discovered the OpenOptions can do the exact things I wanted, and took reference from few sources, so I came out like this:

use chrono::*;
use std::fs::OpenOptions;

fn log_time() -> String {
    let local: DateTime<Local> = Local::now();
    let date = local.format("%a, %d %b %Y %I:%M:%S %p\n").to_string();
    date
}

fn log_time_into_file(filename: String) -> std::io::Result<()> {
    let mut f = OpenOptions::new().append(true).create(true).open(filename);
    f.write_all(log_time().as_bytes());
    Ok(())
}

Upon compiling it threw error message:

error[E0599]: no method named `write_all` found for enum `std::result::Result<std::fs::File, std::io::Error>` in the current scope

Searched for the problem & solution before posting here but no avail. Looked for those tutorial references they can simply get compiled.

I may need help on this, I would like to know did I miss out something or what-else. Thanks!

Please read the pinned code formatting post and post code as formatted text, and not as an image.
EDIT: Thanks :slight_smile:

As for your question, I think you just need to make this change:

// Note the `?` near the end
let mut f = OpenOptions::new().append(true).create(true).open(filename)?;

.open(filename) returns a Result which you have to handle somehow, in case of errors. The ? will return the error if the result is an error, and otherwise the opened file will be assigned to f.

More on the ? operator.

2 Likes

Hi, I have already put the ? near the end of the line:

let mut f = OpenOptions::new().append(true).create(true).open(filename)?;

But I put one more in the next line as well:

f.write_all(log_time().as_bytes())?;

The compiler still complaints about the write_all() method, but with another message now. A full error message here should be more clear:

error[E0599]: no method named `write_all` found for struct `std::fs::File` in the current scope
    --> src/main.rs:17:7
     |
17   |     f.write_all(log_time().as_bytes())?;
     |       ^^^^^^^^^ method not found in `std::fs::File`
     | 
    ::: /home/brian/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/io/mod.rs:1399:8
     |
1399 |     fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
     |        --------- the method is available for `std::boxed::Box<std::fs::File>` here
     |
     = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
     |
1    | use std::io::Write;
     |

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
error: could not compile `rust-first-web`.

To learn more, run the command again with --verbose.

I feel a bit frustrated now, looks like the compiler is telling me the write_all() method belongs to std::fs::File instance, which I did not create an instance of it.

Not sure how can I use OpenOptions instance and append the text in the file.

Just follow the compiler's suggestion, i.e. add use std::io::Write; on top of your file.

OpenOptions::open creates a File when successful.

Yes, I have tried that as well, still the same error message.

However, after I just read the example from std::fs::File documentation thoroughly, I noticed this line:

use std::io::prelude::*;

Then compile again, it succeed with expected behaviour. Seems this library is necessary for write_all() .

I will mark this as my solution here. Thank you all for the quick replies, I appreciate it.

I guess you tried it before fix to handle the Result. Your original code had 2 problems. One is not handling the Result, and another is not importing the trait Write. use std::io::prelude::* imports bunch of traits including the Write. It's an ordinary module so you can see what items are in it via the document.

1 Like

Noted. There are many things I need to learn and understand about Rust programming. Interesting..

Just to elaborate on your own discovery a bit: if you use the standard library’s input or output features, you’ll almost always need to use std::io::prelude::*;. That pulls the BufRead, Read and Write traits into scope, along with their methods, which you’ll usually be using for the actual I/O.

If you search the std:: docs, you’ll see several prelude:: submodules, and the same for many third-party crates. The idea is that you should use _::* these to get the core functionality of the crate or containing module without having to explicitly use every individual component. (The top-level std::prelude is used automatically by the compiler, and provides core stuff like String etc.)

3 Likes