std::io
contains these two public unstable functions:
/// Resets the thread-local stderr handle to the specified writer
///
/// This will replace the current thread's stderr handle, returning the old
/// handle. All future calls to `panic!` and friends will emit their output to
/// this specified handle.
///
/// Note that this does not need to be called for all new threads; the default
/// output handle is to the process's stderr stream.
pub fn set_panic(sink: Box<Write + Send>) -> Option<Box<Write + Send>> { … }
/// Resets the thread-local stdout handle to the specified writer
///
/// This will replace the current thread's stdout handle, returning the old
/// handle. All future calls to `print!` and friends will emit their output to
/// this specified handle.
///
/// Note that this does not need to be called for all new threads; the default
/// output handle is to the process's stdout stream.
pub fn set_print(sink: Box<Write + Send>) -> Option<Box<Write + Send>> { … }
They both have #[doc(hidden)]
. Anyone knows why?
The also both have:
#[unstable(feature = "set_stdio",
reason = "this function may disappear completely or be replaced \
with a more general mechanism",
issue = "0")]
Their functionality is evidently useful, since the test
crate uses them to capture the output of tests on only show it when they fail. External test harnesses would likely want to have similar functionality.
This issue is about eventually stabilizing either these functions or a more general mechanism.
For set_panic
, that mechanism might be panic handlers (#30449) though it would be nice to be able to not duplicate most of the work done in the default handler just to change the output stream. This still leaves set_print
.
This is legacy behavior on behalf of libtest. The set_panic
function should be subsumed by panic::set_handler
, and set_print
is just to have a nice testing experience.
I don't think we should stabilize either of these (as evident by issue = "0"
), and this just requires design work to figure out what we can do to replace this functionality.
@DanielKeep was specifically interested in set_print
as he was working on a library that automatically interprets all escape codes written to stdout and replaces them with calls to the Windows Console API.
Currently, the code for doing this (replacing stdout) is platform-specific and _really_ brittle (see ansi_interpreter
win32/intercept.rs
). It basically has to do an end-run-around behind libstd's back, swapping out the OS level handles and _hoping_ that no one has touched IO yet... because if they _have_, then libstd will have already cached the "true" handles, and _none of this will work_.
Because of libstd's caching behaviour, the _only_ way I can see to do this _properly_ is to be able to _explicitly_ replace the output writer.
Also, consider that you often want to redirect stdout/stderr when working with GUI applications, either to an internal buffer or a log file. Remember that on Windows, GUI applications _don't have_ the standard IO handles connected to anything.
One point to note, however, is that the interception I'm doing is process-wide, whereas I _believe_ set_print
is per-thread. Both are useful, though; something like this would be handy:
fn set_process_stdout<W: Write + IntoRawFd>(write: W) -> io::Result<()> { ... }
fn set_thread_stdout<W: Write>(write: W) -> io::Result<()> { ... }
The two functions in the original report were added as a short term hack and are not intended for stabilisation consideration. If the functionality is desired, an RFC with a proper proposal should be created IMO.
If this is a short term hack, what will the test
crate use long term to capture test output?
What will the test crate use long term to capture test output?
What test crate is using is an implementation detail of (currently) internal component. I suppose test crate would start using the newly introduced stable functions once those become available.
I do not argue against including this sort of functionality into libstd, but to me it sounds like there should be more design process for this and the process in question happens with RFCs (e.g. one obvious solution is to have something based on dup2
on unixes).
For the record, the stdout and stderr are ignored when running an Android app.
android-rs-glue is using this feature to redirect stdout/stderr to Android's logging system.
EDIT: in fact no longer the case.
For the record, see issue #35136 for an example where the test runner fouls up my test via set_print
redirection.
I would prefer to see set_print
and set_panic
replaced by the test runner running its crates in a child process it controls, ala http://gitlab.com/BartMassey/ptyknot, but I'm not sure how that would work on Windows.
set_print() doesn't capture io through std::io::write(). Consider adding set_write() or perhaps making internal set_stdio() , if that would achieve the same result, public would provide io::write() capture.
@SecondNature There is no write()
function in std::io
. Are you referring to something else? (Perhaps provide a code example?)
Yes std::io::write() doesn't exist. My mistake. std::writeln!() redirection is required. Can capture println!() with set_print() but can't capture std::writeln!() as no set_write().
The writeln!
macro takes a "destination" as a first parameter. If that destination is std::io::stdout()
, then the redirection applies today. (Because it is part of stdout()
, unrelated to writeln!
.) When using writeln!
with a different destination, such as an open file, I think the redirection should not apply. Why would it?
We ran into a quirk of set_print
in https://github.com/sebasmagri/env_logger/issues/107. There's some surprising behaviour when using the stdout
and stderr
functions to write to the stream directly during tests, because they aren't using the potentially shimmed LOCAL_STDOUT
and LOCAL_STDERR
and won't be captured. I think we'd definitely need to explore what the role of the set_print
function would be, beyond being a hacked implementation detail so that tests can capture output in a more robust way.
Would like to see these stabilized, so that Rust can produce CloudABI binaries without requiring nightly
@mcandre Could you say more about how and why these functions are required for CloudABI?
Hello,
I think there is a huge difference between an "unstable" feauture such as set_print which has been basically unchanged for years and is used by cargo test and other experimental unstable feautures.
I think either rust should allow certain unstable features to be used in stable as well with a warning, set_print being one example, or set_print should have the unstable removed.
I really need this and it's really a shame that I can't do it at all in rust without being forced to use nightly
I would like to see a stabilized implementation of this so that libraries such as gag would be able to become cross-platform in a reliable way.
I would also love to see that stabilized. The set_print
is insanely useful when targetting WASM, so we can use prints to ad-hoc debug code easily.
I think there is a huge difference between an "unstable" feauture such as set_print which has been basically unchanged for years and is used by cargo test and other experimental unstable feautures.
I'm not really sure set_print
is something we'd actually want stabilized as-is. On the one hand it has been left unchanged for a really long time so seems like it must be stable. On the other hand it's also #[doc(hidden)]
, so is thoroughly undiscoverable and hasn't received much attention.
So rather than stabilize set_print
, I think it would be better to mostly pretend it doesn't exist and explore the design space for configuring a "global print target" from scratch (maybe taking inspiration from global allocators?)
I and other guys agree with @wdanilo - it would be super useful for WASM apps.
See this issue in Seed framework.
Most helpful comment
I and other guys agree with @wdanilo - it would be super useful for WASM apps.
See this issue in Seed framework.