I'm working on a data pipeline node, an application that takes a continuous supply of data from stdin
, processes it, and outputs the result continuously to stdout
in a streaming fashion.
Considering the data interchange format is pre-determined, I need a handy way of barring my debug output from feeding to stdout simultaneously. Essentially, a global lock. True, I could just get rid of all the debug statements, but it's more of an academic exercise.
So let's make a function which can write to stdout
, and locks stdout
as long as it remains in scope so the type system itself prevents other places in the code from writing to stdout
:
use std::io::{self, Write};
pub fn make_push_output<'a>() -> &'a impl Fn(String) -> io::Result<()> {
let handle = io::stdout().lock();
&|output: String| {
handle.write(output.to_string().as_bytes())?;
Ok(())
}
}
Cool, a global lock on stdout
that stays in place until the output push_output()
function goes out of scope, but it doesn't work. I get a whole list of borrow checker errors:
error[E0597]: borrowed value does not live long enough
--> src/lib.rs:4:18
|
4 | let handle = io::stdout().lock();
| ^^^^^^^^^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
|
= note: borrowed value must be valid for the static lifetime...
error[E0597]: borrowed value does not live long enough
--> src/lib.rs:6:6
|
6 | &|output: String| {
| ______^
7 | | handle.write(output.to_string().as_bytes())?;
8 | |
9 | | Ok(())
10 | | }
| |_____^ temporary value does not live long enough
11 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 3:25...
--> src/lib.rs:3:25
|
3 | pub fn make_push_output<'a>() -> &'a impl Fn(String) -> io::Result<()> {
| ^^
error[E0373]: closure may outlive the current function, but it borrows `handle`, which is owned by the current function
--> src/lib.rs:6:6
|
6 | &|output: String| {
| ^^^^^^^^^^^^^^^^ may outlive borrowed value `handle`
7 | handle.write(output.to_string().as_bytes())?;
| ------ `handle` is borrowed here
help: to force the closure to take ownership of `handle` (and any other referenced variables), use the `move` keyword
|
6 | &move |output: String| {
| ^^^^^^^^^^^^^^^^^^^^^
error[E0387]: cannot borrow data mutably in a captured outer variable in an `Fn` closure
--> src/lib.rs:7:9
|
7 | handle.write(output.to_string().as_bytes())?;
| ^^^^^^
|
help: consider changing this closure to take self by mutable reference
--> src/lib.rs:6:6
|
6 | &|output: String| {
| ______^
7 | | handle.write(output.to_string().as_bytes())?;
8 | |
9 | | Ok(())
10| | }
| |_____^
I've been trying for well over an hour to fix this sequence of borrow checker errors on these 7 lines of code. Here's a non-exhaustive list of steps I've taken so far that haven't worked:
- Change the lifetime of
make_push_output
- Add explicit type and lifetime annotation to
handle
- Declare a variable for
io::stdout()
and annotate with type and lifetime - Add explicit type and lifetime annotation to the closure
- Declare a local function instead of using a closure, couldn't capture the environment
- Use
move
semantics on the closure, not the brightest move, but I was grasping at straws