1 | #![cfg_attr (test, allow(unused))] |
2 | |
3 | #[cfg (test)] |
4 | mod tests; |
5 | |
6 | use crate::io::prelude::*; |
7 | |
8 | use crate::cell::{Cell, RefCell}; |
9 | use crate::fmt; |
10 | use crate::fs::File; |
11 | use crate::io::{ |
12 | self, BorrowedCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines, SpecReadByte, |
13 | }; |
14 | use crate::sync::atomic::{AtomicBool, Ordering}; |
15 | use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantMutex, ReentrantMutexGuard}; |
16 | use crate::sys::stdio; |
17 | |
18 | type LocalStream = Arc<Mutex<Vec<u8>>>; |
19 | |
20 | thread_local! { |
21 | /// Used by the test crate to capture the output of the print macros and panics. |
22 | static OUTPUT_CAPTURE: Cell<Option<LocalStream>> = { |
23 | Cell::new(None) |
24 | } |
25 | } |
26 | |
27 | /// Flag to indicate OUTPUT_CAPTURE is used. |
28 | /// |
29 | /// If it is None and was never set on any thread, this flag is set to false, |
30 | /// and OUTPUT_CAPTURE can be safely ignored on all threads, saving some time |
31 | /// and memory registering an unused thread local. |
32 | /// |
33 | /// Note about memory ordering: This contains information about whether a |
34 | /// thread local variable might be in use. Although this is a global flag, the |
35 | /// memory ordering between threads does not matter: we only want this flag to |
36 | /// have a consistent order between set_output_capture and print_to *within |
37 | /// the same thread*. Within the same thread, things always have a perfectly |
38 | /// consistent order. So Ordering::Relaxed is fine. |
39 | static OUTPUT_CAPTURE_USED: AtomicBool = AtomicBool::new(false); |
40 | |
41 | /// A handle to a raw instance of the standard input stream of this process. |
42 | /// |
43 | /// This handle is not synchronized or buffered in any fashion. Constructed via |
44 | /// the `std::io::stdio::stdin_raw` function. |
45 | struct StdinRaw(stdio::Stdin); |
46 | |
47 | /// A handle to a raw instance of the standard output stream of this process. |
48 | /// |
49 | /// This handle is not synchronized or buffered in any fashion. Constructed via |
50 | /// the `std::io::stdio::stdout_raw` function. |
51 | struct StdoutRaw(stdio::Stdout); |
52 | |
53 | /// A handle to a raw instance of the standard output stream of this process. |
54 | /// |
55 | /// This handle is not synchronized or buffered in any fashion. Constructed via |
56 | /// the `std::io::stdio::stderr_raw` function. |
57 | struct StderrRaw(stdio::Stderr); |
58 | |
59 | /// Constructs a new raw handle to the standard input of this process. |
60 | /// |
61 | /// The returned handle does not interact with any other handles created nor |
62 | /// handles returned by `std::io::stdin`. Data buffered by the `std::io::stdin` |
63 | /// handles is **not** available to raw handles returned from this function. |
64 | /// |
65 | /// The returned handle has no external synchronization or buffering. |
66 | #[unstable (feature = "libstd_sys_internals" , issue = "none" )] |
67 | const fn stdin_raw() -> StdinRaw { |
68 | StdinRaw(stdio::Stdin::new()) |
69 | } |
70 | |
71 | /// Constructs a new raw handle to the standard output stream of this process. |
72 | /// |
73 | /// The returned handle does not interact with any other handles created nor |
74 | /// handles returned by `std::io::stdout`. Note that data is buffered by the |
75 | /// `std::io::stdout` handles so writes which happen via this raw handle may |
76 | /// appear before previous writes. |
77 | /// |
78 | /// The returned handle has no external synchronization or buffering layered on |
79 | /// top. |
80 | #[unstable (feature = "libstd_sys_internals" , issue = "none" )] |
81 | const fn stdout_raw() -> StdoutRaw { |
82 | StdoutRaw(stdio::Stdout::new()) |
83 | } |
84 | |
85 | /// Constructs a new raw handle to the standard error stream of this process. |
86 | /// |
87 | /// The returned handle does not interact with any other handles created nor |
88 | /// handles returned by `std::io::stderr`. |
89 | /// |
90 | /// The returned handle has no external synchronization or buffering layered on |
91 | /// top. |
92 | #[unstable (feature = "libstd_sys_internals" , issue = "none" )] |
93 | const fn stderr_raw() -> StderrRaw { |
94 | StderrRaw(stdio::Stderr::new()) |
95 | } |
96 | |
97 | impl Read for StdinRaw { |
98 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
99 | handle_ebadf(self.0.read(buf), 0) |
100 | } |
101 | |
102 | fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { |
103 | handle_ebadf(self.0.read_buf(buf), ()) |
104 | } |
105 | |
106 | fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { |
107 | handle_ebadf(self.0.read_vectored(bufs), 0) |
108 | } |
109 | |
110 | #[inline ] |
111 | fn is_read_vectored(&self) -> bool { |
112 | self.0.is_read_vectored() |
113 | } |
114 | |
115 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
116 | handle_ebadf(self.0.read_to_end(buf), 0) |
117 | } |
118 | |
119 | fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { |
120 | handle_ebadf(self.0.read_to_string(buf), 0) |
121 | } |
122 | } |
123 | |
124 | impl Write for StdoutRaw { |
125 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
126 | handle_ebadf(self.0.write(buf), buf.len()) |
127 | } |
128 | |
129 | fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
130 | let total = bufs.iter().map(|b| b.len()).sum(); |
131 | handle_ebadf(self.0.write_vectored(bufs), total) |
132 | } |
133 | |
134 | #[inline ] |
135 | fn is_write_vectored(&self) -> bool { |
136 | self.0.is_write_vectored() |
137 | } |
138 | |
139 | fn flush(&mut self) -> io::Result<()> { |
140 | handle_ebadf(self.0.flush(), ()) |
141 | } |
142 | |
143 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
144 | handle_ebadf(self.0.write_all(buf), ()) |
145 | } |
146 | |
147 | fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { |
148 | handle_ebadf(self.0.write_all_vectored(bufs), ()) |
149 | } |
150 | |
151 | fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { |
152 | handle_ebadf(self.0.write_fmt(fmt), ()) |
153 | } |
154 | } |
155 | |
156 | impl Write for StderrRaw { |
157 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
158 | handle_ebadf(self.0.write(buf), buf.len()) |
159 | } |
160 | |
161 | fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
162 | let total = bufs.iter().map(|b| b.len()).sum(); |
163 | handle_ebadf(self.0.write_vectored(bufs), total) |
164 | } |
165 | |
166 | #[inline ] |
167 | fn is_write_vectored(&self) -> bool { |
168 | self.0.is_write_vectored() |
169 | } |
170 | |
171 | fn flush(&mut self) -> io::Result<()> { |
172 | handle_ebadf(self.0.flush(), ()) |
173 | } |
174 | |
175 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
176 | handle_ebadf(self.0.write_all(buf), ()) |
177 | } |
178 | |
179 | fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { |
180 | handle_ebadf(self.0.write_all_vectored(bufs), ()) |
181 | } |
182 | |
183 | fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { |
184 | handle_ebadf(self.0.write_fmt(fmt), ()) |
185 | } |
186 | } |
187 | |
188 | fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> { |
189 | match r { |
190 | Err(ref e: &Error) if stdio::is_ebadf(err:e) => Ok(default), |
191 | r: Result => r, |
192 | } |
193 | } |
194 | |
195 | /// A handle to the standard input stream of a process. |
196 | /// |
197 | /// Each handle is a shared reference to a global buffer of input data to this |
198 | /// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods |
199 | /// (e.g., `.lines()`). Reads to this handle are otherwise locked with respect |
200 | /// to other reads. |
201 | /// |
202 | /// This handle implements the `Read` trait, but beware that concurrent reads |
203 | /// of `Stdin` must be executed with care. |
204 | /// |
205 | /// Created by the [`io::stdin`] method. |
206 | /// |
207 | /// [`io::stdin`]: stdin |
208 | /// |
209 | /// ### Note: Windows Portability Considerations |
210 | /// |
211 | /// When operating in a console, the Windows implementation of this stream does not support |
212 | /// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return |
213 | /// an error. |
214 | /// |
215 | /// In a process with a detached console, such as one using |
216 | /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, |
217 | /// the contained handle will be null. In such cases, the standard library's `Read` and |
218 | /// `Write` will do nothing and silently succeed. All other I/O operations, via the |
219 | /// standard library or via raw Windows API calls, will fail. |
220 | /// |
221 | /// # Examples |
222 | /// |
223 | /// ```no_run |
224 | /// use std::io; |
225 | /// |
226 | /// fn main() -> io::Result<()> { |
227 | /// let mut buffer = String::new(); |
228 | /// let stdin = io::stdin(); // We get `Stdin` here. |
229 | /// stdin.read_line(&mut buffer)?; |
230 | /// Ok(()) |
231 | /// } |
232 | /// ``` |
233 | #[stable (feature = "rust1" , since = "1.0.0" )] |
234 | pub struct Stdin { |
235 | inner: &'static Mutex<BufReader<StdinRaw>>, |
236 | } |
237 | |
238 | /// A locked reference to the [`Stdin`] handle. |
239 | /// |
240 | /// This handle implements both the [`Read`] and [`BufRead`] traits, and |
241 | /// is constructed via the [`Stdin::lock`] method. |
242 | /// |
243 | /// ### Note: Windows Portability Considerations |
244 | /// |
245 | /// When operating in a console, the Windows implementation of this stream does not support |
246 | /// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return |
247 | /// an error. |
248 | /// |
249 | /// In a process with a detached console, such as one using |
250 | /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, |
251 | /// the contained handle will be null. In such cases, the standard library's `Read` and |
252 | /// `Write` will do nothing and silently succeed. All other I/O operations, via the |
253 | /// standard library or via raw Windows API calls, will fail. |
254 | /// |
255 | /// # Examples |
256 | /// |
257 | /// ```no_run |
258 | /// use std::io::{self, BufRead}; |
259 | /// |
260 | /// fn main() -> io::Result<()> { |
261 | /// let mut buffer = String::new(); |
262 | /// let stdin = io::stdin(); // We get `Stdin` here. |
263 | /// { |
264 | /// let mut handle = stdin.lock(); // We get `StdinLock` here. |
265 | /// handle.read_line(&mut buffer)?; |
266 | /// } // `StdinLock` is dropped here. |
267 | /// Ok(()) |
268 | /// } |
269 | /// ``` |
270 | #[must_use = "if unused stdin will immediately unlock" ] |
271 | #[stable (feature = "rust1" , since = "1.0.0" )] |
272 | pub struct StdinLock<'a> { |
273 | inner: MutexGuard<'a, BufReader<StdinRaw>>, |
274 | } |
275 | |
276 | /// Constructs a new handle to the standard input of the current process. |
277 | /// |
278 | /// Each handle returned is a reference to a shared global buffer whose access |
279 | /// is synchronized via a mutex. If you need more explicit control over |
280 | /// locking, see the [`Stdin::lock`] method. |
281 | /// |
282 | /// ### Note: Windows Portability Considerations |
283 | /// |
284 | /// When operating in a console, the Windows implementation of this stream does not support |
285 | /// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return |
286 | /// an error. |
287 | /// |
288 | /// In a process with a detached console, such as one using |
289 | /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, |
290 | /// the contained handle will be null. In such cases, the standard library's `Read` and |
291 | /// `Write` will do nothing and silently succeed. All other I/O operations, via the |
292 | /// standard library or via raw Windows API calls, will fail. |
293 | /// |
294 | /// # Examples |
295 | /// |
296 | /// Using implicit synchronization: |
297 | /// |
298 | /// ```no_run |
299 | /// use std::io; |
300 | /// |
301 | /// fn main() -> io::Result<()> { |
302 | /// let mut buffer = String::new(); |
303 | /// io::stdin().read_line(&mut buffer)?; |
304 | /// Ok(()) |
305 | /// } |
306 | /// ``` |
307 | /// |
308 | /// Using explicit synchronization: |
309 | /// |
310 | /// ```no_run |
311 | /// use std::io::{self, BufRead}; |
312 | /// |
313 | /// fn main() -> io::Result<()> { |
314 | /// let mut buffer = String::new(); |
315 | /// let stdin = io::stdin(); |
316 | /// let mut handle = stdin.lock(); |
317 | /// |
318 | /// handle.read_line(&mut buffer)?; |
319 | /// Ok(()) |
320 | /// } |
321 | /// ``` |
322 | #[must_use ] |
323 | #[stable (feature = "rust1" , since = "1.0.0" )] |
324 | pub fn stdin() -> Stdin { |
325 | static INSTANCE: OnceLock<Mutex<BufReader<StdinRaw>>> = OnceLock::new(); |
326 | Stdin { |
327 | inner: INSTANCE.get_or_init(|| { |
328 | Mutex::new(BufReader::with_capacity(capacity:stdio::STDIN_BUF_SIZE, inner:stdin_raw())) |
329 | }), |
330 | } |
331 | } |
332 | |
333 | impl Stdin { |
334 | /// Locks this handle to the standard input stream, returning a readable |
335 | /// guard. |
336 | /// |
337 | /// The lock is released when the returned lock goes out of scope. The |
338 | /// returned guard also implements the [`Read`] and [`BufRead`] traits for |
339 | /// accessing the underlying data. |
340 | /// |
341 | /// # Examples |
342 | /// |
343 | /// ```no_run |
344 | /// use std::io::{self, BufRead}; |
345 | /// |
346 | /// fn main() -> io::Result<()> { |
347 | /// let mut buffer = String::new(); |
348 | /// let stdin = io::stdin(); |
349 | /// let mut handle = stdin.lock(); |
350 | /// |
351 | /// handle.read_line(&mut buffer)?; |
352 | /// Ok(()) |
353 | /// } |
354 | /// ``` |
355 | #[stable (feature = "rust1" , since = "1.0.0" )] |
356 | pub fn lock(&self) -> StdinLock<'static> { |
357 | // Locks this handle with 'static lifetime. This depends on the |
358 | // implementation detail that the underlying `Mutex` is static. |
359 | StdinLock { inner: self.inner.lock().unwrap_or_else(|e| e.into_inner()) } |
360 | } |
361 | |
362 | /// Locks this handle and reads a line of input, appending it to the specified buffer. |
363 | /// |
364 | /// For detailed semantics of this method, see the documentation on |
365 | /// [`BufRead::read_line`]. |
366 | /// |
367 | /// # Examples |
368 | /// |
369 | /// ```no_run |
370 | /// use std::io; |
371 | /// |
372 | /// let mut input = String::new(); |
373 | /// match io::stdin().read_line(&mut input) { |
374 | /// Ok(n) => { |
375 | /// println!("{n} bytes read" ); |
376 | /// println!("{input}" ); |
377 | /// } |
378 | /// Err(error) => println!("error: {error}" ), |
379 | /// } |
380 | /// ``` |
381 | /// |
382 | /// You can run the example one of two ways: |
383 | /// |
384 | /// - Pipe some text to it, e.g., `printf foo | path/to/executable` |
385 | /// - Give it text interactively by running the executable directly, |
386 | /// in which case it will wait for the Enter key to be pressed before |
387 | /// continuing |
388 | #[stable (feature = "rust1" , since = "1.0.0" )] |
389 | pub fn read_line(&self, buf: &mut String) -> io::Result<usize> { |
390 | self.lock().read_line(buf) |
391 | } |
392 | |
393 | /// Consumes this handle and returns an iterator over input lines. |
394 | /// |
395 | /// For detailed semantics of this method, see the documentation on |
396 | /// [`BufRead::lines`]. |
397 | /// |
398 | /// # Examples |
399 | /// |
400 | /// ```no_run |
401 | /// use std::io; |
402 | /// |
403 | /// let lines = io::stdin().lines(); |
404 | /// for line in lines { |
405 | /// println!("got a line: {}" , line.unwrap()); |
406 | /// } |
407 | /// ``` |
408 | #[must_use = "`self` will be dropped if the result is not used" ] |
409 | #[stable (feature = "stdin_forwarders" , since = "1.62.0" )] |
410 | pub fn lines(self) -> Lines<StdinLock<'static>> { |
411 | self.lock().lines() |
412 | } |
413 | } |
414 | |
415 | #[stable (feature = "std_debug" , since = "1.16.0" )] |
416 | impl fmt::Debug for Stdin { |
417 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
418 | f.debug_struct(name:"Stdin" ).finish_non_exhaustive() |
419 | } |
420 | } |
421 | |
422 | #[stable (feature = "rust1" , since = "1.0.0" )] |
423 | impl Read for Stdin { |
424 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
425 | self.lock().read(buf) |
426 | } |
427 | fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { |
428 | self.lock().read_buf(buf) |
429 | } |
430 | fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { |
431 | self.lock().read_vectored(bufs) |
432 | } |
433 | #[inline ] |
434 | fn is_read_vectored(&self) -> bool { |
435 | self.lock().is_read_vectored() |
436 | } |
437 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
438 | self.lock().read_to_end(buf) |
439 | } |
440 | fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { |
441 | self.lock().read_to_string(buf) |
442 | } |
443 | fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { |
444 | self.lock().read_exact(buf) |
445 | } |
446 | } |
447 | |
448 | // only used by platform-dependent io::copy specializations, i.e. unused on some platforms |
449 | #[cfg (any(target_os = "linux" , target_os = "android" ))] |
450 | impl StdinLock<'_> { |
451 | pub(crate) fn as_mut_buf(&mut self) -> &mut BufReader<impl Read> { |
452 | &mut self.inner |
453 | } |
454 | } |
455 | |
456 | #[stable (feature = "rust1" , since = "1.0.0" )] |
457 | impl Read for StdinLock<'_> { |
458 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
459 | self.inner.read(buf) |
460 | } |
461 | |
462 | fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> io::Result<()> { |
463 | self.inner.read_buf(buf) |
464 | } |
465 | |
466 | fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { |
467 | self.inner.read_vectored(bufs) |
468 | } |
469 | |
470 | #[inline ] |
471 | fn is_read_vectored(&self) -> bool { |
472 | self.inner.is_read_vectored() |
473 | } |
474 | |
475 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
476 | self.inner.read_to_end(buf) |
477 | } |
478 | |
479 | fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { |
480 | self.inner.read_to_string(buf) |
481 | } |
482 | |
483 | fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { |
484 | self.inner.read_exact(buf) |
485 | } |
486 | } |
487 | |
488 | impl SpecReadByte for StdinLock<'_> { |
489 | #[inline ] |
490 | fn spec_read_byte(&mut self) -> Option<io::Result<u8>> { |
491 | BufReader::spec_read_byte(&mut *self.inner) |
492 | } |
493 | } |
494 | |
495 | #[stable (feature = "rust1" , since = "1.0.0" )] |
496 | impl BufRead for StdinLock<'_> { |
497 | fn fill_buf(&mut self) -> io::Result<&[u8]> { |
498 | self.inner.fill_buf() |
499 | } |
500 | |
501 | fn consume(&mut self, n: usize) { |
502 | self.inner.consume(amt:n) |
503 | } |
504 | |
505 | fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> { |
506 | self.inner.read_until(byte, buf) |
507 | } |
508 | |
509 | fn read_line(&mut self, buf: &mut String) -> io::Result<usize> { |
510 | self.inner.read_line(buf) |
511 | } |
512 | } |
513 | |
514 | #[stable (feature = "std_debug" , since = "1.16.0" )] |
515 | impl fmt::Debug for StdinLock<'_> { |
516 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
517 | f.debug_struct(name:"StdinLock" ).finish_non_exhaustive() |
518 | } |
519 | } |
520 | |
521 | /// A handle to the global standard output stream of the current process. |
522 | /// |
523 | /// Each handle shares a global buffer of data to be written to the standard |
524 | /// output stream. Access is also synchronized via a lock and explicit control |
525 | /// over locking is available via the [`lock`] method. |
526 | /// |
527 | /// Created by the [`io::stdout`] method. |
528 | /// |
529 | /// ### Note: Windows Portability Considerations |
530 | /// |
531 | /// When operating in a console, the Windows implementation of this stream does not support |
532 | /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return |
533 | /// an error. |
534 | /// |
535 | /// In a process with a detached console, such as one using |
536 | /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, |
537 | /// the contained handle will be null. In such cases, the standard library's `Read` and |
538 | /// `Write` will do nothing and silently succeed. All other I/O operations, via the |
539 | /// standard library or via raw Windows API calls, will fail. |
540 | /// |
541 | /// [`lock`]: Stdout::lock |
542 | /// [`io::stdout`]: stdout |
543 | #[stable (feature = "rust1" , since = "1.0.0" )] |
544 | pub struct Stdout { |
545 | // FIXME: this should be LineWriter or BufWriter depending on the state of |
546 | // stdout (tty or not). Note that if this is not line buffered it |
547 | // should also flush-on-panic or some form of flush-on-abort. |
548 | inner: &'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>, |
549 | } |
550 | |
551 | /// A locked reference to the [`Stdout`] handle. |
552 | /// |
553 | /// This handle implements the [`Write`] trait, and is constructed via |
554 | /// the [`Stdout::lock`] method. See its documentation for more. |
555 | /// |
556 | /// ### Note: Windows Portability Considerations |
557 | /// |
558 | /// When operating in a console, the Windows implementation of this stream does not support |
559 | /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return |
560 | /// an error. |
561 | /// |
562 | /// In a process with a detached console, such as one using |
563 | /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, |
564 | /// the contained handle will be null. In such cases, the standard library's `Read` and |
565 | /// `Write` will do nothing and silently succeed. All other I/O operations, via the |
566 | /// standard library or via raw Windows API calls, will fail. |
567 | #[must_use = "if unused stdout will immediately unlock" ] |
568 | #[stable (feature = "rust1" , since = "1.0.0" )] |
569 | pub struct StdoutLock<'a> { |
570 | inner: ReentrantMutexGuard<'a, RefCell<LineWriter<StdoutRaw>>>, |
571 | } |
572 | |
573 | static STDOUT: OnceLock<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = OnceLock::new(); |
574 | |
575 | /// Constructs a new handle to the standard output of the current process. |
576 | /// |
577 | /// Each handle returned is a reference to a shared global buffer whose access |
578 | /// is synchronized via a mutex. If you need more explicit control over |
579 | /// locking, see the [`Stdout::lock`] method. |
580 | /// |
581 | /// ### Note: Windows Portability Considerations |
582 | /// |
583 | /// When operating in a console, the Windows implementation of this stream does not support |
584 | /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return |
585 | /// an error. |
586 | /// |
587 | /// In a process with a detached console, such as one using |
588 | /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, |
589 | /// the contained handle will be null. In such cases, the standard library's `Read` and |
590 | /// `Write` will do nothing and silently succeed. All other I/O operations, via the |
591 | /// standard library or via raw Windows API calls, will fail. |
592 | /// |
593 | /// # Examples |
594 | /// |
595 | /// Using implicit synchronization: |
596 | /// |
597 | /// ```no_run |
598 | /// use std::io::{self, Write}; |
599 | /// |
600 | /// fn main() -> io::Result<()> { |
601 | /// io::stdout().write_all(b"hello world" )?; |
602 | /// |
603 | /// Ok(()) |
604 | /// } |
605 | /// ``` |
606 | /// |
607 | /// Using explicit synchronization: |
608 | /// |
609 | /// ```no_run |
610 | /// use std::io::{self, Write}; |
611 | /// |
612 | /// fn main() -> io::Result<()> { |
613 | /// let stdout = io::stdout(); |
614 | /// let mut handle = stdout.lock(); |
615 | /// |
616 | /// handle.write_all(b"hello world" )?; |
617 | /// |
618 | /// Ok(()) |
619 | /// } |
620 | /// ``` |
621 | #[must_use ] |
622 | #[stable (feature = "rust1" , since = "1.0.0" )] |
623 | #[cfg_attr (not(test), rustc_diagnostic_item = "io_stdout" )] |
624 | pub fn stdout() -> Stdout { |
625 | Stdout { |
626 | inner: STDOUTOnceLock>> |
627 | .get_or_init(|| ReentrantMutex::new(RefCell::new(LineWriter::new(inner:stdout_raw())))), |
628 | } |
629 | } |
630 | |
631 | // Flush the data and disable buffering during shutdown |
632 | // by replacing the line writer by one with zero |
633 | // buffering capacity. |
634 | pub fn cleanup() { |
635 | let mut initialized: bool = false; |
636 | let stdout: &ReentrantMutex>> = STDOUT.get_or_init(|| { |
637 | initialized = true; |
638 | ReentrantMutex::new(RefCell::new(LineWriter::with_capacity(capacity:0, inner:stdout_raw()))) |
639 | }); |
640 | |
641 | if !initialized { |
642 | // The buffer was previously initialized, overwrite it here. |
643 | // We use try_lock() instead of lock(), because someone |
644 | // might have leaked a StdoutLock, which would |
645 | // otherwise cause a deadlock here. |
646 | if let Some(lock: ReentrantMutexGuard<'_, RefCell<…>>) = stdout.try_lock() { |
647 | *lock.borrow_mut() = LineWriter::with_capacity(capacity:0, inner:stdout_raw()); |
648 | } |
649 | } |
650 | } |
651 | |
652 | impl Stdout { |
653 | /// Locks this handle to the standard output stream, returning a writable |
654 | /// guard. |
655 | /// |
656 | /// The lock is released when the returned lock goes out of scope. The |
657 | /// returned guard also implements the `Write` trait for writing data. |
658 | /// |
659 | /// # Examples |
660 | /// |
661 | /// ```no_run |
662 | /// use std::io::{self, Write}; |
663 | /// |
664 | /// fn main() -> io::Result<()> { |
665 | /// let mut stdout = io::stdout().lock(); |
666 | /// |
667 | /// stdout.write_all(b"hello world" )?; |
668 | /// |
669 | /// Ok(()) |
670 | /// } |
671 | /// ``` |
672 | #[stable (feature = "rust1" , since = "1.0.0" )] |
673 | pub fn lock(&self) -> StdoutLock<'static> { |
674 | // Locks this handle with 'static lifetime. This depends on the |
675 | // implementation detail that the underlying `ReentrantMutex` is |
676 | // static. |
677 | StdoutLock { inner: self.inner.lock() } |
678 | } |
679 | } |
680 | |
681 | #[stable (feature = "std_debug" , since = "1.16.0" )] |
682 | impl fmt::Debug for Stdout { |
683 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
684 | f.debug_struct(name:"Stdout" ).finish_non_exhaustive() |
685 | } |
686 | } |
687 | |
688 | #[stable (feature = "rust1" , since = "1.0.0" )] |
689 | impl Write for Stdout { |
690 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
691 | (&*self).write(buf) |
692 | } |
693 | fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
694 | (&*self).write_vectored(bufs) |
695 | } |
696 | #[inline ] |
697 | fn is_write_vectored(&self) -> bool { |
698 | io::Write::is_write_vectored(&&*self) |
699 | } |
700 | fn flush(&mut self) -> io::Result<()> { |
701 | (&*self).flush() |
702 | } |
703 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
704 | (&*self).write_all(buf) |
705 | } |
706 | fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { |
707 | (&*self).write_all_vectored(bufs) |
708 | } |
709 | fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { |
710 | (&*self).write_fmt(args) |
711 | } |
712 | } |
713 | |
714 | #[stable (feature = "write_mt" , since = "1.48.0" )] |
715 | impl Write for &Stdout { |
716 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
717 | self.lock().write(buf) |
718 | } |
719 | fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
720 | self.lock().write_vectored(bufs) |
721 | } |
722 | #[inline ] |
723 | fn is_write_vectored(&self) -> bool { |
724 | self.lock().is_write_vectored() |
725 | } |
726 | fn flush(&mut self) -> io::Result<()> { |
727 | self.lock().flush() |
728 | } |
729 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
730 | self.lock().write_all(buf) |
731 | } |
732 | fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { |
733 | self.lock().write_all_vectored(bufs) |
734 | } |
735 | fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { |
736 | self.lock().write_fmt(args) |
737 | } |
738 | } |
739 | |
740 | #[stable (feature = "rust1" , since = "1.0.0" )] |
741 | impl Write for StdoutLock<'_> { |
742 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
743 | self.inner.borrow_mut().write(buf) |
744 | } |
745 | fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
746 | self.inner.borrow_mut().write_vectored(bufs) |
747 | } |
748 | #[inline ] |
749 | fn is_write_vectored(&self) -> bool { |
750 | self.inner.borrow_mut().is_write_vectored() |
751 | } |
752 | fn flush(&mut self) -> io::Result<()> { |
753 | self.inner.borrow_mut().flush() |
754 | } |
755 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
756 | self.inner.borrow_mut().write_all(buf) |
757 | } |
758 | fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { |
759 | self.inner.borrow_mut().write_all_vectored(bufs) |
760 | } |
761 | } |
762 | |
763 | #[stable (feature = "std_debug" , since = "1.16.0" )] |
764 | impl fmt::Debug for StdoutLock<'_> { |
765 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
766 | f.debug_struct(name:"StdoutLock" ).finish_non_exhaustive() |
767 | } |
768 | } |
769 | |
770 | /// A handle to the standard error stream of a process. |
771 | /// |
772 | /// For more information, see the [`io::stderr`] method. |
773 | /// |
774 | /// [`io::stderr`]: stderr |
775 | /// |
776 | /// ### Note: Windows Portability Considerations |
777 | /// |
778 | /// When operating in a console, the Windows implementation of this stream does not support |
779 | /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return |
780 | /// an error. |
781 | /// |
782 | /// In a process with a detached console, such as one using |
783 | /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, |
784 | /// the contained handle will be null. In such cases, the standard library's `Read` and |
785 | /// `Write` will do nothing and silently succeed. All other I/O operations, via the |
786 | /// standard library or via raw Windows API calls, will fail. |
787 | #[stable (feature = "rust1" , since = "1.0.0" )] |
788 | pub struct Stderr { |
789 | inner: &'static ReentrantMutex<RefCell<StderrRaw>>, |
790 | } |
791 | |
792 | /// A locked reference to the [`Stderr`] handle. |
793 | /// |
794 | /// This handle implements the [`Write`] trait and is constructed via |
795 | /// the [`Stderr::lock`] method. See its documentation for more. |
796 | /// |
797 | /// ### Note: Windows Portability Considerations |
798 | /// |
799 | /// When operating in a console, the Windows implementation of this stream does not support |
800 | /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return |
801 | /// an error. |
802 | /// |
803 | /// In a process with a detached console, such as one using |
804 | /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, |
805 | /// the contained handle will be null. In such cases, the standard library's `Read` and |
806 | /// `Write` will do nothing and silently succeed. All other I/O operations, via the |
807 | /// standard library or via raw Windows API calls, will fail. |
808 | #[must_use = "if unused stderr will immediately unlock" ] |
809 | #[stable (feature = "rust1" , since = "1.0.0" )] |
810 | pub struct StderrLock<'a> { |
811 | inner: ReentrantMutexGuard<'a, RefCell<StderrRaw>>, |
812 | } |
813 | |
814 | /// Constructs a new handle to the standard error of the current process. |
815 | /// |
816 | /// This handle is not buffered. |
817 | /// |
818 | /// ### Note: Windows Portability Considerations |
819 | /// |
820 | /// When operating in a console, the Windows implementation of this stream does not support |
821 | /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return |
822 | /// an error. |
823 | /// |
824 | /// In a process with a detached console, such as one using |
825 | /// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, |
826 | /// the contained handle will be null. In such cases, the standard library's `Read` and |
827 | /// `Write` will do nothing and silently succeed. All other I/O operations, via the |
828 | /// standard library or via raw Windows API calls, will fail. |
829 | /// |
830 | /// # Examples |
831 | /// |
832 | /// Using implicit synchronization: |
833 | /// |
834 | /// ```no_run |
835 | /// use std::io::{self, Write}; |
836 | /// |
837 | /// fn main() -> io::Result<()> { |
838 | /// io::stderr().write_all(b"hello world" )?; |
839 | /// |
840 | /// Ok(()) |
841 | /// } |
842 | /// ``` |
843 | /// |
844 | /// Using explicit synchronization: |
845 | /// |
846 | /// ```no_run |
847 | /// use std::io::{self, Write}; |
848 | /// |
849 | /// fn main() -> io::Result<()> { |
850 | /// let stderr = io::stderr(); |
851 | /// let mut handle = stderr.lock(); |
852 | /// |
853 | /// handle.write_all(b"hello world" )?; |
854 | /// |
855 | /// Ok(()) |
856 | /// } |
857 | /// ``` |
858 | #[must_use ] |
859 | #[stable (feature = "rust1" , since = "1.0.0" )] |
860 | #[cfg_attr (not(test), rustc_diagnostic_item = "io_stderr" )] |
861 | pub fn stderr() -> Stderr { |
862 | // Note that unlike `stdout()` we don't use `at_exit` here to register a |
863 | // destructor. Stderr is not buffered, so there's no need to run a |
864 | // destructor for flushing the buffer |
865 | static INSTANCE: ReentrantMutex<RefCell<StderrRaw>> = |
866 | ReentrantMutex::new(RefCell::new(stderr_raw())); |
867 | |
868 | Stderr { inner: &INSTANCE } |
869 | } |
870 | |
871 | impl Stderr { |
872 | /// Locks this handle to the standard error stream, returning a writable |
873 | /// guard. |
874 | /// |
875 | /// The lock is released when the returned lock goes out of scope. The |
876 | /// returned guard also implements the [`Write`] trait for writing data. |
877 | /// |
878 | /// # Examples |
879 | /// |
880 | /// ``` |
881 | /// use std::io::{self, Write}; |
882 | /// |
883 | /// fn foo() -> io::Result<()> { |
884 | /// let stderr = io::stderr(); |
885 | /// let mut handle = stderr.lock(); |
886 | /// |
887 | /// handle.write_all(b"hello world" )?; |
888 | /// |
889 | /// Ok(()) |
890 | /// } |
891 | /// ``` |
892 | #[stable (feature = "rust1" , since = "1.0.0" )] |
893 | pub fn lock(&self) -> StderrLock<'static> { |
894 | // Locks this handle with 'static lifetime. This depends on the |
895 | // implementation detail that the underlying `ReentrantMutex` is |
896 | // static. |
897 | StderrLock { inner: self.inner.lock() } |
898 | } |
899 | } |
900 | |
901 | #[stable (feature = "std_debug" , since = "1.16.0" )] |
902 | impl fmt::Debug for Stderr { |
903 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
904 | f.debug_struct(name:"Stderr" ).finish_non_exhaustive() |
905 | } |
906 | } |
907 | |
908 | #[stable (feature = "rust1" , since = "1.0.0" )] |
909 | impl Write for Stderr { |
910 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
911 | (&*self).write(buf) |
912 | } |
913 | fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
914 | (&*self).write_vectored(bufs) |
915 | } |
916 | #[inline ] |
917 | fn is_write_vectored(&self) -> bool { |
918 | io::Write::is_write_vectored(&&*self) |
919 | } |
920 | fn flush(&mut self) -> io::Result<()> { |
921 | (&*self).flush() |
922 | } |
923 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
924 | (&*self).write_all(buf) |
925 | } |
926 | fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { |
927 | (&*self).write_all_vectored(bufs) |
928 | } |
929 | fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { |
930 | (&*self).write_fmt(args) |
931 | } |
932 | } |
933 | |
934 | #[stable (feature = "write_mt" , since = "1.48.0" )] |
935 | impl Write for &Stderr { |
936 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
937 | self.lock().write(buf) |
938 | } |
939 | fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
940 | self.lock().write_vectored(bufs) |
941 | } |
942 | #[inline ] |
943 | fn is_write_vectored(&self) -> bool { |
944 | self.lock().is_write_vectored() |
945 | } |
946 | fn flush(&mut self) -> io::Result<()> { |
947 | self.lock().flush() |
948 | } |
949 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
950 | self.lock().write_all(buf) |
951 | } |
952 | fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { |
953 | self.lock().write_all_vectored(bufs) |
954 | } |
955 | fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> { |
956 | self.lock().write_fmt(args) |
957 | } |
958 | } |
959 | |
960 | #[stable (feature = "rust1" , since = "1.0.0" )] |
961 | impl Write for StderrLock<'_> { |
962 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
963 | self.inner.borrow_mut().write(buf) |
964 | } |
965 | fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { |
966 | self.inner.borrow_mut().write_vectored(bufs) |
967 | } |
968 | #[inline ] |
969 | fn is_write_vectored(&self) -> bool { |
970 | self.inner.borrow_mut().is_write_vectored() |
971 | } |
972 | fn flush(&mut self) -> io::Result<()> { |
973 | self.inner.borrow_mut().flush() |
974 | } |
975 | fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
976 | self.inner.borrow_mut().write_all(buf) |
977 | } |
978 | fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { |
979 | self.inner.borrow_mut().write_all_vectored(bufs) |
980 | } |
981 | } |
982 | |
983 | #[stable (feature = "std_debug" , since = "1.16.0" )] |
984 | impl fmt::Debug for StderrLock<'_> { |
985 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
986 | f.debug_struct(name:"StderrLock" ).finish_non_exhaustive() |
987 | } |
988 | } |
989 | |
990 | /// Sets the thread-local output capture buffer and returns the old one. |
991 | #[unstable ( |
992 | feature = "internal_output_capture" , |
993 | reason = "this function is meant for use in the test crate \ |
994 | and may disappear in the future" , |
995 | issue = "none" |
996 | )] |
997 | #[doc (hidden)] |
998 | pub fn set_output_capture(sink: Option<LocalStream>) -> Option<LocalStream> { |
999 | if sink.is_none() && !OUTPUT_CAPTURE_USED.load(order:Ordering::Relaxed) { |
1000 | // OUTPUT_CAPTURE is definitely None since OUTPUT_CAPTURE_USED is false. |
1001 | return None; |
1002 | } |
1003 | OUTPUT_CAPTURE_USED.store(val:true, order:Ordering::Relaxed); |
1004 | OUTPUT_CAPTURE.with(move |slot: &Cell| slot.replace(val:sink)) |
1005 | } |
1006 | |
1007 | /// Write `args` to the capture buffer if enabled and possible, or `global_s` |
1008 | /// otherwise. `label` identifies the stream in a panic message. |
1009 | /// |
1010 | /// This function is used to print error messages, so it takes extra |
1011 | /// care to avoid causing a panic when `OUTPUT_CAPTURE` is unusable. |
1012 | /// For instance, if the TLS key for output capturing is already destroyed, or |
1013 | /// if the local stream is in use by another thread, it will just fall back to |
1014 | /// the global stream. |
1015 | /// |
1016 | /// However, if the actual I/O causes an error, this function does panic. |
1017 | /// |
1018 | /// Writing to non-blocking stdout/stderr can cause an error, which will lead |
1019 | /// this function to panic. |
1020 | fn print_to<T>(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str) |
1021 | where |
1022 | T: Write, |
1023 | { |
1024 | if print_to_buffer_if_capture_used(args) { |
1025 | // Successfully wrote to capture buffer. |
1026 | return; |
1027 | } |
1028 | |
1029 | if let Err(e: Error) = global_s().write_fmt(args) { |
1030 | panic!("failed printing to {label}: {e}" ); |
1031 | } |
1032 | } |
1033 | |
1034 | fn print_to_buffer_if_capture_used(args: fmt::Arguments<'_>) -> bool { |
1035 | OUTPUT_CAPTURE_USED.load(order:Ordering::Relaxed) |
1036 | && OUTPUT_CAPTURE.try_with(|s: &Cell| { |
1037 | // Note that we completely remove a local sink to write to in case |
1038 | // our printing recursively panics/prints, so the recursive |
1039 | // panic/print goes to the global sink instead of our local sink. |
1040 | s.take().map(|w: Arc>>| { |
1041 | let _ = w.lock().unwrap_or_else(|e: PoisonError>| e.into_inner()).write_fmt(args); |
1042 | s.set(val:Some(w)); |
1043 | }) |
1044 | }) == Ok(Some(())) |
1045 | } |
1046 | |
1047 | /// Used by impl Termination for Result to print error after `main` or a test |
1048 | /// has returned. Should avoid panicking, although we can't help it if one of |
1049 | /// the Display impls inside args decides to. |
1050 | pub(crate) fn attempt_print_to_stderr(args: fmt::Arguments<'_>) { |
1051 | if print_to_buffer_if_capture_used(args) { |
1052 | return; |
1053 | } |
1054 | |
1055 | // Ignore error if the write fails, for example because stderr is already |
1056 | // closed. There is not much point panicking at this point. |
1057 | let _ = stderr().write_fmt(args); |
1058 | } |
1059 | |
1060 | /// Trait to determine if a descriptor/handle refers to a terminal/tty. |
1061 | #[stable (feature = "is_terminal" , since = "1.70.0" )] |
1062 | pub trait IsTerminal: crate::sealed::Sealed { |
1063 | /// Returns `true` if the descriptor/handle refers to a terminal/tty. |
1064 | /// |
1065 | /// On platforms where Rust does not know how to detect a terminal yet, this will return |
1066 | /// `false`. This will also return `false` if an unexpected error occurred, such as from |
1067 | /// passing an invalid file descriptor. |
1068 | /// |
1069 | /// # Platform-specific behavior |
1070 | /// |
1071 | /// On Windows, in addition to detecting consoles, this currently uses some heuristics to |
1072 | /// detect older msys/cygwin/mingw pseudo-terminals based on device name: devices with names |
1073 | /// starting with `msys-` or `cygwin-` and ending in `-pty` will be considered terminals. |
1074 | /// Note that this [may change in the future][changes]. |
1075 | /// |
1076 | /// [changes]: io#platform-specific-behavior |
1077 | #[stable (feature = "is_terminal" , since = "1.70.0" )] |
1078 | fn is_terminal(&self) -> bool; |
1079 | } |
1080 | |
1081 | macro_rules! impl_is_terminal { |
1082 | ($($t:ty),*$(,)?) => {$( |
1083 | #[unstable(feature = "sealed" , issue = "none" )] |
1084 | impl crate::sealed::Sealed for $t {} |
1085 | |
1086 | #[stable(feature = "is_terminal" , since = "1.70.0" )] |
1087 | impl IsTerminal for $t { |
1088 | #[inline] |
1089 | fn is_terminal(&self) -> bool { |
1090 | crate::sys::io::is_terminal(self) |
1091 | } |
1092 | } |
1093 | )*} |
1094 | } |
1095 | |
1096 | impl_is_terminal!(File, Stdin, StdinLock<'_>, Stdout, StdoutLock<'_>, Stderr, StderrLock<'_>); |
1097 | |
1098 | #[unstable ( |
1099 | feature = "print_internals" , |
1100 | reason = "implementation detail which may disappear or be replaced at any time" , |
1101 | issue = "none" |
1102 | )] |
1103 | #[doc (hidden)] |
1104 | #[cfg (not(test))] |
1105 | pub fn _print(args: fmt::Arguments<'_>) { |
1106 | print_to(args, global_s:stdout, label:"stdout" ); |
1107 | } |
1108 | |
1109 | #[unstable ( |
1110 | feature = "print_internals" , |
1111 | reason = "implementation detail which may disappear or be replaced at any time" , |
1112 | issue = "none" |
1113 | )] |
1114 | #[doc (hidden)] |
1115 | #[cfg (not(test))] |
1116 | pub fn _eprint(args: fmt::Arguments<'_>) { |
1117 | print_to(args, global_s:stderr, label:"stderr" ); |
1118 | } |
1119 | |
1120 | #[cfg (test)] |
1121 | pub use realstd::io::{_eprint, _print}; |
1122 | |