Question about implementation of print macros

Hello :crab:,

I have a question about the implementation of print macros in Rust.
It seems that the Stdout used by print! and println! macros is wrapped by a
RefCell, and locks are also used to provide thread safety in writing to Stdout.

/* From: /src/libstd/io/ */

thread_local! {
    /// Stdout used by print! and println! macros
    static LOCAL_STDOUT: RefCell<Option<Box<dyn Write + Send>>> = {

/* From: src/libstd/io/ */

#[stable(feature = "rust1", since = "1.0.0")]
impl Write for Stdout {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
    fn flush(&mut self) -> io::Result<()> {
    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {

It also seems that eventually, writing to stdout in Rust invokes a call to libc::write as below.
/* From: src/libstd/sys/unix/ */

pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
    let ret = cvt(unsafe {
        libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), max_len()))
    Ok(ret as usize)

I picked up from the web that the write() function in glibc is thread-safe.
In that case, (confining just to Unix/Linux) is it safe to say that print macros in Rust suffer from redundant synchronization?

  • synchronization in the Rust wrapping around invocation of libc::write
  • synchronization inside the implementation of libc::write)

Please correct me if I have misunderstood anything.. Thank you for reading.

It is thread-safe in glibc, but not necessrily so in any other libc implementation. I can't find this info right now, but it's entirely possible that musl, for example, doesn't synchronize internally. And anyway, if this thread-safety is not a part of libc interface guarantees (i.e. it's not required to be so), we can't rely on the implementation.


Each call to write is not guaranteed to write the full buffer. So writing a single line may end up requiring several calls to write (this is what write_all does). So by locking on the full write_all, the entire line is guaranteed to be output without being interleaved with other lines printed by other threads.


This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.