Simple borrow question

I am trying to create a simple file logger using the log facade, but I feel like I dug myself into a hole and can not find the correct way to get out.

I want the logger to hold an handle on the file it is writing too.

However, to write to the file, I should be able to modify the Option, but the log Trait is using a fn log(&self, record: &LogRecord) and not a fn log(&mut self, ...)

So I feel like my approach is wrong, what would be a better way ?


    extern crate log;

     use std::error::error;
     use std::fs::file;
     use std::path::path;
     use std::io::write;

     use log::{logrecord, loglevel, loglevelfilter, setloggererror, logmetadata};

    pub struct simplelogger {
        logfile: option<file>,
    }

    impl log::log for simplelogger {
         fn enabled(&self, metadata: &logmetadata) -> bool {
           metadata.level() <= loglevel::info
    }

    fn log(&self, record: &logrecord) {
        if self.enabled(record.metadata()) {
            // println!("{} - {}", record.level(), record.args());

            let mut buffer = vec::new();
            write!(&mut buffer, "{} - {}", record.level(), record.args());

            match self.logfile {
                some( file) => { //does not work because should be a ref mut 
                    match file.write(buffer.as_slice()) {
                        err(why) => panic!("couldn't write: {}", error::description(&why)),
                        ok(_) => (),
                    }
                }
                none => panic!("logfile does not exist!"),
            };
        }
      }
    }

    impl simplelogger {
        pub fn init() -> result<(), setloggererror> {

        let path = path::new("log/trace.log");
        let display = path.display();


        // open a file in write-only mode, returns `io::result<file>`
        let mut file = match file::create(&path) {
            err(why) => panic!("couldn't create {}: {}", display, error::description(&why)),
            ok(file) => file,
        };


        log::set_logger(|max_log_level| {
            max_log_level.set(loglevelfilter::info);
            box::new(simplelogger { logfile: some(file) })
        })
      }
   }

You probably want to use interior mutability here

https://doc.rust-lang.org/book/mutability.html

The thing is that & and &mut really mean "shared" and "unique" (or "alisable" and "not aliased"), rather then "immutable" and "mutable". There even was a proposal to rename mut to uniq.

And though you normally don't want to mutate something that is shared, sometimes you need to, and cells and Mutexes exist for this use case.

2 Likes

Thanks for your input. I slept over it and realized I was trying to do something terrible anyway, writing the same file from potentially multiple threads without exclusion mechanism.

I will either use something like a Mutex<RefCell<>> or use a queue to single a writer thread.

I think there is no need in both Mutex and RefCell. If you have &Mutex<T>, you can get &mut T.

That's why Rust is awesome :slight_smile:

Yes, that was I thought also. I was frustrated by the compiler until I realized, it was just preventing me to shoot myself in the foot. Indeed, this is amazing !

3 Likes