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: Given the following code: The current output is: I think it's typical for a newcomer to want to get a iterator by chaining method calls like . There's no obvious reason why that shouldn't work. The examples in the documentation do typically show being assigned to a variable prior to calling its method, but don't explain why that's necessary. I think the "borrow" terminology is misleading, but there might not be a concise way to describe what's actually going on to the user. The "temporary value is freed at the end of this statement" is probably correct. The "creates a temporary which is freed while still in use" is probably more correctly "creates a temporary that is required to outlive (or live exactly as long as) the result of...", followed by a pointer to the method invocation, with text of " method call". Maybe it should also point out the lifetime argument in the return type. I tried and failed to find a satisfactory explanation of what exactly was borrowing from the temporary value produced by . When I looked through the library source code, the only references in were those created by locking a , and the in question is static. I'm guessing the existence of a borrow is assumed rather than inferred from analyzing the function body of . The assumption might be the result of the anonymous lifetime argument on the output type from , which imposes a requirement that the doesn't outlive (or has the same lifetime as?) the value that is the receiver of . The correct fix might be to change the locked stdin handles to all use lifetimes, but I will probably file that as a separate issue.
label +A-borrow-checker +A-lifetimes +D-confusing +D-incorrect +D-newcomer-roadblock
IMO the intent is clear here. Without changing the API, why exactly is Rust unable to lift 'a &'static X to 'static & 'static X, if X also contains only 'static lifetimes? I don't think raw references have a meaningful dropper; borrowck is able to freely resolve any derived lifetimes on impls that don't depend on anything else to 'static; I'm pretty sure under current rules we should be able to substitute latter for earlier and get exactly the same behaviour.
This is due to .