An asynchronous logger I've written a simple asynchronous logger (playground):

mod logger {

    use std::sync::mpsc::{channel, Sender};
    use time::now;

    pub type LogError = std::sync::mpsc::SendError&lt;String&gt;;

    /// Runs a function that can logs asynchronously.
    pub fn log&lt;F, T&gt;(f: F) -&gt; T
    where
        F: FnOnce(Logger) -&gt; T,
    {
        let (sender, receiver) = channel();
        let handle = std::thread::spawn(move || {
            while let Ok(msg) = receiver.recv() {
                println!("{}", msg);
            }
            println!("LOG FINISHED");
        });
        let result = f(Logger { sender });

        handle.join().unwrap(); // Error handling?

        result
    }

    /// The actual logger.
    #[derive(Clone)]
    pub struct Logger {
        sender: Sender&lt;String&gt;,
    }

    impl Logger {
        /// Logs an information.
        pub fn info(&amp;self, s: impl std::fmt::Display) -&gt; Result&lt;(), LogError&gt; {
            let s = format!("[{}] [INFO ] {}", now().rfc3339(), s);

            self.sender.send(s)
        }

        /// Logs an error.
        pub fn error(&amp;self, s: impl std::fmt::Display) -&gt; Result&lt;(), LogError&gt; {
            let s = format!("[{}] [ERROR] {}", now().rfc3339(), s);

            self.sender.send(s)
        }
    }

}

// Demontration:
fn main() -&gt; Result&lt;(), logger::LogError&gt; {
    logger::log(|logger| {
        logger.info("Hello world")?;

        // Cloning without overhead:
        logger.clone().error("Oops, error")
    })
}


The concept is simple: I spawn a thread with a receiver, and the logger sends the text to it.

To make this work, I was forced to use a callback to correctly join the thread when the user's code is done. Is there a better/prettier way to do that?

And also, is that pattern efficient? I know that I can buffer the lines inside the spawned thread, but I am talking about the whole pattern.
I have found a way to use a runtime struct instead of a closure. Logger needs to borrow the runtime, and it must implement Drop so that Runtime::drop is not called before Logger::drop.

mod logger {

    use std::marker::PhantomData;
    use std::sync::mpsc::{channel, Sender};
    use std::thread::JoinHandle;
    use time::now;

    pub type LogError = std::sync::mpsc::SendError&lt;String&gt;;

    pub struct Runtime {
        sender: Option&lt;Sender&lt;String&gt;&gt;,
        thread_handle: Option&lt;JoinHandle&lt;()&gt;&gt;,
    }

    impl Runtime {
        pub fn new() -&gt; Self {
            let (sender, receiver) = channel();
            let thread_handle = std::thread::spawn(move || {
                while let Ok(msg) = receiver.recv() {
                    println!("{}", msg);
                }
                println!("LOG FINISHED");
            });

            Runtime {
                sender: Some(sender),
                thread_handle: Some(thread_handle),
            }
        }

        pub fn logger(&amp;self) -&gt; Logger {
            Logger {
                sender: self.sender.clone().unwrap(),
                _marker: PhantomData,
            }
        }
    }

    impl Drop for Runtime {
        fn drop(&amp;mut self) {
            // Removes the last sender alive, so that the thread quits.
            let _ = self.sender.take();

            if let Some(handle) = self.thread_handle.take() {
                if let Err(e) = handle.join() {
                    eprintln!("Error while exiting the logger manager: {:?}", e);
                }
            }
        }
    }

    /// The actual logger.
    #[derive(Clone)]
    pub struct Logger&lt;'a&gt; {
        sender: Sender&lt;String&gt;,
        _marker: PhantomData&lt;&amp;'a ()&gt;,
    }

    impl Logger&lt;'_&gt; {
        /// Logs an information.
        pub fn info(&amp;self, s: impl std::fmt::Display) -&gt; Result&lt;(), LogError&gt; {
            let s = format!("[{}] [INFO ] {}", now().rfc3339(), s);

            self.sender.send(s)
        }

        /// Logs an error.
        pub fn error(&amp;self, s: impl std::fmt::Display) -&gt; Result&lt;(), LogError&gt; {
            let s = format!("[{}] [ERROR] {}", now().rfc3339(), s);

            self.sender.send(s)
        }
    }

    impl Drop for Logger&lt;'_&gt; {
        fn drop(&amp;mut self) {
            // The non-trivial drop prevents the logger to outlives the manager.
        }
    }
}

// Demontration:
fn main() -&gt; Result&lt;(), logger::LogError&gt; {
    let log_manager = logger::Runtime::new();
    let logger = log_manager.logger();

    logger.info("Hello world")?;
    // Cloning without overhead:
    logger.clone().error("Oops, error")?;

    Ok(())
}


If one try to make a logger outlive the runtime, it will not compile:

fn main() -&gt; Result&lt;(), logger::LogError&gt; {
    let log_manager = logger::Runtime::new();
    let logger = log_manager.logger();

    logger.clone().info("Hello world")
}


Gives:

error[E0597]: `log_manager` does not live long enough
  --&gt; src/main.rs:85:18
   |
85 |     let logger = log_manager.logger();
   |                  ^^^^^^^^^^^ borrowed value does not live long enough
86 | 
87 |     logger.clone().info("Hello world")
   |     -------------- a temporary with access to the borrow is created here ...
88 | }
   | -
   | |
   | `log_manager` dropped here while still borrowed
   | ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `logger::Logger`
   |
   = note: The temporary is part of an expression at the end of a block. Consider forcing this temporary to be dropped sooner, before the block's local variables are dropped. For example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block.