A few problems:

No newline. This is really gross. The format needs some work. It appears that the old value is being erased by the new value.

Let's verify #3 by fixing the format. If the time changes between runs, then we will know that's what is happening.

The format method of DateTime uses the standard strftime formatting conventions. Ideally, I would like times to be something like:

Sat, Jun 6 2015 05:32:00 PM Sun, Jun 7 2015 08:35:00 AM

…etc. This should be readable enough for me to use. After reading the documentation for a while, I've come up with this:

extern crate chrono; use std::io::prelude::*; use std::fs::File; use std::io; use chrono::*; fn log_time(filename: &'static str) -> io::Result<()> { let local: DateTime<Local> = Local::now(); let formatted = local.format("%a, %b %d %Y %I:%M:%S %p

").to_string(); let bytes = formatted.as_bytes(); let mut f = try!(File::create(filename)); try!(f.write_all(bytes)); Ok(()) } fn main() { match log_time("log.txt") { Ok(..) => println!("File created!"), Err(..) => println!("Error: could not create file.") } }

Testing it:

$ rm log.txt $ cargo run Running `target/debug/simple-log` File created! $ cat log.txt Sun, Jun 07 2015 06:37:21 PM $ sleep 5; cargo run Running `target/debug/simple-log` File created! $ cat log.txt Sun, Jun 07 2015 06:37:41 PM

So, clearly the program is overwriting the log entries, which tbqh is what I expect, as I remember the documentation for File::create specifying that this is what would happen. So, I need to look at the documentation for manipulating files again.

I did some searching around, and basically finding the answer to this isn't trivial. After a while I found the documentation for std::path::Path, which has an exists method.

At this point, the interactions between types in my application is becoming increasingly hard to manage. I feel nervous, so I will commit before continuing.

I want to pull the time entry string generation out of the log_time function because it seems like the entry formatting/creation is distinct from the file manipulation code. So, trying this:

extern crate chrono; use std::io::prelude::*; use std::fs::File; use std::io; use chrono::*; fn log_time_entry() -> String { let local: DateTime<Local> = Local::now(); let formatted = local.format("%a, %b %d %Y %I:%M:%S %p

").to_string(); formatted } fn log_time(filename: &'static str) -> io::Result<()> { let bytes = log_time_entry().as_bytes(); let mut f = try!(File::create(filename)); try!(f.write_all(bytes)); Ok(()) } fn main() { match log_time("log.txt") { Ok(..) => println!("File created!"), Err(..) => println!("Error: could not create file.") } }

=>

$ cargo run Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log) src/main.rs:16:17: 16:33 error: borrowed value does not live long enough src/main.rs:16 let bytes = log_time_entry().as_bytes(); ^~~~~~~~~~~~~~~~ src/main.rs:16:45: 20:2 note: reference must be valid for the block suffix following statement 0 at 16:44... src/main.rs:16 let bytes = log_time_entry().as_bytes(); src/main.rs:17 let mut f = try!(File::create(filename)); src/main.rs:18 try!(f.write_all(bytes)); src/main.rs:19 Ok(()) src/main.rs:20 } src/main.rs:16:5: 16:45 note: ...but borrowed value is only valid for the statement at 16:4 src/main.rs:16 let bytes = log_time_entry().as_bytes(); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/main.rs:16:5: 16:45 help: consider using a `let` binding to increase its lifetime src/main.rs:16 let bytes = log_time_entry().as_bytes(); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to previous error Could not compile `simple-log`. To learn more, run the command again with --verbose.

So, this looks just like the problem I had earlier. Does borrowing/ownership require that a function have an explicit reference to resources? That seems a little strange. I will try to fix it again:

extern crate chrono; use std::io::prelude::*; use std::fs::File; use std::io; use chrono::*; fn formatted_time_entry() -> String { let local: DateTime<Local> = Local::now(); let formatted = local.format("%a, %b %d %Y %I:%M:%S %p

").to_string(); formatted } fn log_time(filename: &'static str) -> io::Result<()> { let entry = formatted_time_entry(); let bytes = entry.as_bytes(); let mut f = try!(File::create(filename)); try!(f.write_all(bytes)); Ok(()) } fn main() { match log_time("log.txt") { Ok(..) => println!("File created!"), Err(..) => println!("Error: could not create file.") } }

=>

$ cargo run Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log) Running `target/debug/simple-log` File created!

So, adding an explicit reference seems to be the solution. Whatever. It is an easy rule to learn and follow.

Next I want to extract the file manipulation code to its own function:

extern crate chrono; use std::io::prelude::*; use std::fs::File; use std::io; use chrono::*; fn formatted_time_entry() -> String { let local: DateTime<Local> = Local::now(); let formatted = local.format("%a, %b %d %Y %I:%M:%S %p

").to_string(); formatted } fn record_entry_in_log(filename: &str, bytes: &[u8]) -> io::Result<()> { let mut f = try!(File::create(filename)); try!(f.write_all(bytes)); Ok(()) } fn log_time(filename: &'static str) -> io::Result<()> { let entry = formatted_time_entry(); let bytes = entry.as_bytes(); try!(record_entry_in_log(filename, &bytes)); Ok(()) } fn main() { match log_time("log.txt") { Ok(..) => println!("File created!"), Err(..) => println!("Error: could not create file.") } }

And this works. I made some initial errors, but they were quickly corrected and was all stuff that has been covered here before.

Looking into the documentation for std::fs::File, I notice a reference to std::fs::OpenOptions, which is exactly what I have been looking for. It would definitely be better than using std::path .

My first attempt:

extern crate chrono; use std::io::prelude::*; use std::fs::{File,OpenOptions}; use std::io; use chrono::{DateTime,Local}; fn formatted_time_entry() -> String { let local: DateTime<Local> = Local::now(); let formatted = local.format("%a, %b %d %Y %I:%M:%S %p

").to_string(); formatted } fn record_entry_in_log(filename: &str, bytes: &[u8]) -> io::Result<()> { let mut file = try!(OpenOptions::new(). append(true). create(true). open(filename)); try!(file.write_all(bytes)); Ok(()) } fn log_time(filename: &'static str) -> io::Result<()> { let entry = formatted_time_entry(); let bytes = entry.as_bytes(); try!(record_entry_in_log(filename, &bytes)); Ok(()) } fn main() { match log_time("log.txt") { Ok(..) => println!("File created!"), Err(..) => println!("Error: could not create file.") } }

=>

$ cargo run Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log) src/main.rs:4:15: 4:19 warning: unused import, #[warn(unused_imports)] on by default src/main.rs:4 use std::fs::{File,OpenOptions}; ^~~~ Running `target/debug/simple-log` Error: could not create file.

Interesting. I see that it is actually creating the file, after which I notice this is the message I've hard-coded into main . Ugh; I think this will work:

extern crate chrono; use std::io::prelude::*; use std::fs::{File,OpenOptions}; use std::io; use chrono::{DateTime,Local}; fn formatted_time_entry() -> String { let local: DateTime<Local> = Local::now(); let formatted = local.format("%a, %b %d %Y %I:%M:%S %p

").to_string(); formatted } fn record_entry_in_log(filename: &str, bytes: &[u8]) -> io::Result<()> { let mut file = try!(OpenOptions::new(). append(true). create(true). open(filename)); try!(file.write_all(bytes)); Ok(()) } fn log_time(filename: &'static str) -> io::Result<()> { let entry = formatted_time_entry(); let bytes = entry.as_bytes(); try!(record_entry_in_log(filename, &bytes)); Ok(()) } fn main() { match log_time("log.txt") { Ok(..) => println!("File created!"), Err(e) => println!("Error: {}", e) } }

=>

$ cargo run Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log) src/main.rs:4:15: 4:19 warning: unused import, #[warn(unused_imports)] on by default src/main.rs:4 use std::fs::{File,OpenOptions}; ^~~~ Running `target/debug/simple-log` Error: Bad file descriptor (os error 9)

Weird. Searching for this "bad file descriptor" error message seems to indicate that this happens when a file descriptor is used has been closed. what happens if I comment out the file.write_all call?

$ rm log.txt $ cargo run Compiling simple-log v0.1.0 (file:///Users/joel/Projects/simple-log) src/main.rs:3:5: 3:25 warning: unused import, #[warn(unused_imports)] on by default src/main.rs:3 use std::io::prelude::*; ^~~~~~~~~~~~~~~~~~~~ src/main.rs:4:15: 4:19 warning: unused import, #[warn(unused_imports)] on by default src/main.rs:4 use std::fs::{File,OpenOptions}; ^~~~ src/main.rs:15:40: 15:45 warning: unused variable: `bytes`, #[warn(unused_variables)] on by default src/main.rs:15 fn record_entry_in_log(filename: &str, bytes: &[u8]) -> io::Result<()> { ^~~~~ src/main.rs:16:9: 16:17 warning: unused variable: `file`, #[warn(unused_variables)] on by default src/main.rs:16 let mut file = try!(OpenOptions::new(). ^~~~~~~~ src/main.rs:16:9: 16:17 warning: variable does not need to be mutable, #[warn(unused_mut)] on by de fault src/main.rs:16 let mut file = try!(OpenOptions::new(). ^~~~~~~~ Running `target/debug/simple-log` File created! $ ls Cargo.lock Cargo.toml log.txt src target

Unsurprisingly, there are a bunch unused messages, but aside from that the file is indeed created.

It seems a little silly, but I tried adding .write(true) to the chain of functions, and it worked. It seems like .append(true) should imply .write(true) , but I guess it doesn't.

And with that, its working! The final version:

extern crate chrono; use std::io::prelude::*; use std::fs::{File,OpenOptions}; use std::io; use chrono::{DateTime,Local}; fn formatted_time_entry() -> String { let local: DateTime<Local> = Local::now(); let formatted = local.format("%a, %b %d %Y %I:%M:%S %p

").to_string(); formatted } fn record_entry_in_log(filename: &str, bytes: &[u8]) -> io::Result<()> { let mut file = try!(OpenOptions::new(). append(true). write(true). create(true). open(filename)); try!(file.write_all(bytes)); Ok(()) } fn log_time(filename: &'static str) -> io::Result<()> { let entry = formatted_time_entry(); let bytes = entry.as_bytes(); try!(record_entry_in_log(filename, &bytes)); Ok(()) } fn main() { match log_time("log.txt") { Ok(..) => println!("File created!"), Err(e) => println!("Error: {}", e) } }

=>

$ ls Cargo.lock Cargo.toml src target $ cargo run Running `target/debug/simple-log` File created! $ cargo run Running `target/debug/simple-log` File created! $ cat log.txt Sun, Jun 07 2015 10:40:01 PM Sun, Jun 07 2015 10:40:05 PM