Code change:
let _a = stderr(); let _a = _a.lock(); } <ins> #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn test_lock_stderr() { test_lock(stderr, stderr_locked); } #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn test_lock_stdin() { test_lock(stdin, stdin_locked); } #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn test_lock_stdout() { test_lock(stdout, stdout_locked); } // Helper trait to make lock testing function generic. trait Stdio<'a>: 'static where Self::Lock: 'a, { type Lock; fn lock(&'a self) -> Self::Lock; } impl<'a> Stdio<'a> for Stderr { type Lock = StderrLock<'a>; fn lock(&'a self) -> StderrLock<'a> { self.lock() } } impl<'a> Stdio<'a> for Stdin { type Lock = StdinLock<'a>; fn lock(&'a self) -> StdinLock<'a> { self.lock() } } impl<'a> Stdio<'a> for Stdout { type Lock = StdoutLock<'a>; fn lock(&'a self) -> StdoutLock<'a> { self.lock() } } // Helper trait to make lock testing function generic. trait StdioOwnedLock: 'static {} impl StdioOwnedLock for StderrLock<'static> {} impl StdioOwnedLock for StdinLock<'static> {} impl StdioOwnedLock for StdoutLock<'static> {} // Tests locking on stdio handles by starting two threads and checking that // they block each other appropriately. fn test_lock<T, U>(get_handle: fn() -> T, get_locked: fn() -> U) where T: for<'a> Stdio<'a>, U: StdioOwnedLock, { // State enum to track different phases of the test, primarily when // each lock is acquired and released. #[derive(Debug, PartialEq)] enum State { Start1, Acquire1, Start2, Release1, Acquire2, Release2, } use State::*; // Logging vector to be checked to make sure lock acquisitions and // releases happened in the correct order. let log = Arc::new(Mutex::new(Vec::new())); let ((tx1, rx1), (tx2, rx2)) = (sync_channel(0), sync_channel(0)); let th1 = { let (log, tx) = (Arc::clone(&log), tx1); thread::spawn(move || { log.lock().unwrap().push(Start1); let handle = get_handle(); { let locked = handle.lock(); log.lock().unwrap().push(Acquire1); tx.send(Acquire1).unwrap(); // notify of acquisition tx.send(Release1).unwrap(); // wait for release command log.lock().unwrap().push(Release1); } tx.send(Acquire1).unwrap(); // wait for th2 acquire { let locked = handle.lock(); log.lock().unwrap().push(Acquire1); } log.lock().unwrap().push(Release1); }) }; let th2 = { let (log, tx) = (Arc::clone(&log), tx2); thread::spawn(move || { tx.send(Start2).unwrap(); // wait for start command let locked = get_locked(); log.lock().unwrap().push(Acquire2); tx.send(Acquire2).unwrap(); // notify of acquisition tx.send(Release2).unwrap(); // wait for release command log.lock().unwrap().push(Release2); }) }; assert_eq!(rx1.recv().unwrap(), Acquire1); // wait for th1 acquire log.lock().unwrap().push(Start2); assert_eq!(rx2.recv().unwrap(), Start2); // block th2 assert_eq!(rx1.recv().unwrap(), Release1); // release th1 assert_eq!(rx2.recv().unwrap(), Acquire2); // wait for th2 acquire assert_eq!(rx1.recv().unwrap(), Acquire1); // block th1 assert_eq!(rx2.recv().unwrap(), Release2); // release th2 th2.join().unwrap(); th1.join().unwrap(); assert_eq!( *log.lock().unwrap(), [Start1, Acquire1, Start2, Release1, Acquire2, Release2, Acquire1, Release1] ); } </ins>

Related discussion:
text: is declared as follows: Because there is only one input lifetime (the elided lifetime of ), it is assigned to the output lifetime , which is why the lock can live at most as long as the temporary.
Thanks. I think this might be a bug in libstd (using instead of as the return type), but I'll file that separately. What I think is a diagnostic inaccuracy is that the error message refers to a borrow, when, by inspection of the library source code, no borrow is taking place in . (Unless I'm missing something?) What I think is happening is that the compiler is enforcing that lives no longer than the temporary produced by , and is misleadingly calling that a borrow in the error message.
As a simplified example: The error message refers to a borrow even when the output type of does not refer to at all!
that's because of the elision rules: on the declaration you're implicitly telling the compiler that the lifetime of the borrow matches the lifetime of the type. .
Thanks. I think the error messages could use a little more detail about how something that the borrow checker sees as a borrow may be an artifact of lifetime constraints in a function signature rather than an actual borrow in the function body. It certainly surprised me the first time I ran into it. (Maybe only in the explanatory text in the compiler error index. It looks like E0597 could also benefit from that detail, because it's a similar case, and an example in E0716 produces E0597.)
Shorter example, probably better suited for an example in the compiler error index: