Change:
Over time the value of may change (e.g. see ) and we should likely reload the value over time instead of caching the return value to catch cases like this.
There's very little value in caching the result. takes 3 nanoseconds for me according to a quick bench.

Discussion:
// INVALID_HANDLE_VALUE. Stdio::Inherit => { match stdio::get(stdio_id) { <del> Ok(io) => io.handle().duplicate(0, true, c::DUPLICATE_SAME_ACCESS), </del> <ins> Ok(io) => { let io = Handle::new(io.handle()); let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); io.into_raw(); return ret } </ins> Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)), } }

use sys::handle::Handle; use sys_common::io::read_to_end_uninitialized; <del> pub struct NoClose(Option<Handle>); </del> pub enum Output { <del> Console(NoClose), Pipe(NoClose), </del> <ins> Console(c::HANDLE), Pipe(c::HANDLE), </ins> } pub struct Stdin { <del> handle: Output, </del> utf8: Mutex<io::Cursor<Vec<u8>>>, } <del> pub struct Stdout(Output); pub struct Stderr(Output); </del> <ins> pub struct Stdout; pub struct Stderr; </ins> pub fn get(handle: c::DWORD) -> io::Result<Output> { let handle = unsafe { c::GetStdHandle(handle) }; if handle == c::INVALID_HANDLE_VALUE { Err(io::Error::last_os_error()) } else if handle.is_null() { <del> Err(io::Error::new(io::ErrorKind::Other, "no stdio handle available for this process")) </del> <ins> Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32)) </ins> } else { <del> let ret = NoClose::new(handle); </del> let mut out = 0; match unsafe { c::GetConsoleMode(handle, &mut out) } { <del> 0 => Ok(Output::Pipe(ret)), _ => Ok(Output::Console(ret)), </del> <ins> 0 => Ok(Output::Pipe(handle)), _ => Ok(Output::Console(handle)), </ins> } } } <del> fn write(out: &Output, data: &[u8]) -> io::Result<usize> { let handle = match *out { Output::Console(ref c) => c.get().raw(), Output::Pipe(ref p) => return p.get().write(data), </del> <ins> fn write(handle: c::DWORD, data: &[u8]) -> io::Result<usize> { let handle = match try!(get(handle)) { Output::Console(c) => c, Output::Pipe(p) => { let handle = Handle::new(p); let ret = handle.write(data); handle.into_raw(); return ret } </ins> }; // As with stdin on windows, stdout often can't handle writes of large // sizes. For an example, see #14940. For this reason, don't try to // write the entire output buffer on windows.

impl Stdin { pub fn new() -> io::Result<Stdin> { <del> get(c::STD_INPUT_HANDLE).map(|handle| { Stdin { handle: handle, utf8: Mutex::new(Cursor::new(Vec::new())), } </del> <ins> Ok(Stdin { utf8: Mutex::new(Cursor::new(Vec::new())), </ins> }) } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { <del> let handle = match self.handle { Output::Console(ref c) => c.get().raw(), Output::Pipe(ref p) => return p.get().read(buf), </del> <ins> let handle = match try!(get(c::STD_INPUT_HANDLE)) { Output::Console(c) => c, Output::Pipe(p) => { let handle = Handle::new(p); let ret = handle.read(buf); handle.into_raw(); return ret } </ins> }; let mut utf8 = self.utf8.lock().unwrap(); // Read more if the buffer is empty

Ok(utf8) => utf8.into_bytes(), Err(..) => return Err(invalid_encoding()), }; <del> if let Output::Console(_) = self.handle { if let Some(&last_byte) = data.last() { if last_byte == CTRL_Z { data.pop(); } </del> <ins> if let Some(&last_byte) = data.last() { if last_byte == CTRL_Z { data.pop(); </ins> } } *utf8 = Cursor::new(data);

impl Stdout { pub fn new() -> io::Result<Stdout> { <del> get(c::STD_OUTPUT_HANDLE).map(Stdout) </del> <ins> Ok(Stdout) </ins> } pub fn write(&self, data: &[u8]) -> io::Result<usize> { <del> write(&self.0, data) </del> <ins> write(c::STD_OUTPUT_HANDLE, data) </ins> } pub fn flush(&self) -> io::Result<()> {

impl Stderr { pub fn new() -> io::Result<Stderr> { <del> get(c::STD_ERROR_HANDLE).map(Stderr) </del> <ins> Ok(Stderr) </ins> } pub fn write(&self, data: &[u8]) -> io::Result<usize> { <del> write(&self.0, data) </del> <ins> write(c::STD_ERROR_HANDLE, data) </ins> } pub fn flush(&self) -> io::Result<()> {

} } <del> impl NoClose { fn new(handle: c::HANDLE) -> NoClose { NoClose(Some(Handle::new(handle))) } fn get(&self) -> &Handle { self.0.as_ref().unwrap() } } impl Drop for NoClose { fn drop(&mut self) { self.0.take().unwrap().into_raw(); } } </del> impl Output { <del> pub fn handle(&self) -> &Handle { let nc = match *self { Output::Console(ref c) => c, Output::Pipe(ref c) => c, }; nc.0.as_ref().unwrap() </del> <ins> pub fn handle(&self) -> c::HANDLE { match *self { Output::Console(c) => c, Output::Pipe(c) => c, } </ins> } }

<ins> // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. #![feature(rustc_private)] extern crate rustc_back; use std::fs::File; use std::io::{Read, Write}; use rustc_back::tempdir::TempDir; #[cfg(unix)] fn switch_stdout_to(file: File) { use std::os::unix::prelude::*; extern { fn dup2(old: i32, new: i32) -> i32; } unsafe { assert_eq!(dup2(file.as_raw_fd(), 1), 1); } } #[cfg(windows)] fn switch_stdout_to(file: File) { use std::os::windows::prelude::*; extern "system" { fn SetStdHandle(nStdHandle: u32, handle: *mut u8) -> i32; } const STD_OUTPUT_HANDLE: u32 = (-11i32) as u32; unsafe { let rc = SetStdHandle(STD_OUTPUT_HANDLE, file.into_raw_handle() as *mut _); assert!(rc != 0); } } fn main() { let td = TempDir::new("foo").unwrap(); let path = td.path().join("bar"); let f = File::create(&path).unwrap(); println!("foo"); std::io::stdout().flush().unwrap(); switch_stdout_to(f); println!("bar"); std::io::stdout().flush().unwrap(); let mut contents = String::new(); File::open(&path).unwrap().read_to_string(&mut contents).unwrap(); assert_eq!(contents, "barn"); } </ins>