Avoid evaluating dbg!() arguments in a closure.

The dbg!() macro should behave like a function call in how it evaluates
its arguments, and captures by Rust closures are not fully compatible
with path-specific borrowing. Specifically:

    let borrow = &mut obj.foo;
    dbg!("{}", obj.bar);

would fail because the closure inside dbg!() would borrow all of obj
instead of just obj.foo.

Fix this by using the format_args!() macro to evaluate the dbg!()
arguments and produce an fmt::Arguments object which can be safely
passed to the thread-local closure for printing.

The arguments are still evaluated inside an if { .. } which
constant-folds away in release builds.
This commit is contained in:
Jakob Stoklund Olesen
2017-08-04 08:44:17 -07:00
parent e0fd5252d5
commit 81ff9bac72

View File

@@ -15,7 +15,7 @@ use std::env;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt; use std::fmt;
use std::fs::File; use std::fs::File;
use std::io::{Write, BufWriter}; use std::io::{self, Write};
use std::sync::atomic; use std::sync::atomic;
use std::thread; use std::thread;
@@ -56,20 +56,18 @@ fn initialize() -> bool {
} }
thread_local! { thread_local! {
static WRITER : RefCell<BufWriter<File>> = RefCell::new(open_file()); static WRITER : RefCell<io::BufWriter<File>> = RefCell::new(open_file());
} }
/// Execute a closure with mutable access to the tracing file writer. /// Write a line with the given format arguments.
/// ///
/// This is for use by the `dbg!` macro. /// This is for use by the `dbg!` macro.
pub fn with_writer<F, R>(f: F) -> R pub fn writeln_with_format_args(args: fmt::Arguments) -> io::Result<()> {
where F: FnOnce(&mut Write) -> R WRITER.with(|rc| writeln!(*rc.borrow_mut(), "{}", args))
{
WRITER.with(|rc| f(&mut *rc.borrow_mut()))
} }
/// Open the tracing file for the current thread. /// Open the tracing file for the current thread.
fn open_file() -> BufWriter<File> { fn open_file() -> io::BufWriter<File> {
let file = match thread::current().name() { let file = match thread::current().name() {
None => File::create("cretonne.dbg"), None => File::create("cretonne.dbg"),
Some(name) => { Some(name) => {
@@ -83,7 +81,7 @@ fn open_file() -> BufWriter<File> {
} }
} }
.expect("Can't open tracing file"); .expect("Can't open tracing file");
BufWriter::new(file) io::BufWriter::new(file)
} }
/// Write a line to the debug trace file if tracing is enabled. /// Write a line to the debug trace file if tracing is enabled.
@@ -95,7 +93,7 @@ macro_rules! dbg {
if $crate::dbg::enabled() { if $crate::dbg::enabled() {
// Drop the error result so we don't get compiler errors for ignoring it. // Drop the error result so we don't get compiler errors for ignoring it.
// What are you going to do, log the error? // What are you going to do, log the error?
$crate::dbg::with_writer(|w| { writeln!(w, $($arg)+).ok(); }) $crate::dbg::writeln_with_format_args(format_args!($($arg)+)).ok();
} }
} }
} }