To Consume or Reference in Rust
February 06, 2026
Recently, shapr and I were working on some code that demands a Write implementation.
We’ve attempted to distill the example down to its essence – hopefully retaining the interesting part!
The original implementation “consumed” the Write implementer. This worked fine – we passed off our File and never looked at it again.
However, when trying to write tests, it’s convenient to use something in-memory that also implements Write, like Vec<u8>.
This reveals the core problem: our “output data” has been consumed by the function, and our test can no longer refer to it.
The Options
There seem to be two main options for this situation:
- “consume” the
Writebut return it back out; or - receive and use a mutable reference, so we can refer to it after
We are not sure which option is the most idiomatic for Rust programmers.
Lets look at some actual code.
The Actual Example
In our “real” code there’s a “builder” pattern and a lot of other stuff doing cryptographic operations; here, we simply uppercase a string and write it out.
use std::io::Write;
pub fn consume_unconsume<W>(mut output: W, text: &str) -> W
where
W: Write
{
let upper = text.to_uppercase();
output.write(upper.as_str().as_bytes()).unwrap();
output
}
pub fn reference<W>(output: &mut W, text: &str) -> ()
where
W: Write
{
let upper = text.to_uppercase();
output.write(upper.as_str().as_bytes()).unwrap();
}
#[test]
fn test_example() {
let data0: Vec<u8> = vec![];
let mut data1: Vec<u8> = vec![];
let text = "attack at dawn";
let data0 = consume_unconsume(data0, text);
// our original issue is that we did something like this, but
// didn't return back out the "Write", which works fine unless you
// try to refer to it later. Try swapping in this line instead of
// the above line to see the Rust error you get
////consume_unconsume(data0, text);
assert_eq!(data0.len(), text.len());
reference(&mut data1, &text);
assert_eq!(data1.len(), text.len());
}
Okay, so the essence of the problem is visible in the comment above: you can get away without “passing back out” the consumed Write instance until you try to refer to the data later.
So when using File, it’s not as apparent: even in tests, you often don’t refer to the same File instance but instead load the saved file as a completely new File instance.
Solution One: Mutable Reference + Lifetime
One solution is to do the thing that reference() above does: take in a reference. This is all fine and good when there’s just one function involved, but if you save the reference in a struct, you need lifetime parameters on the struct (to ensure the Write instance lives long enough).
Solution Two: Consume, But Return
The other solution is to “consume” the Write instance, as the consume_unconsume() function does.
This has some advantages:
- no need for a lifetime parameter (any struct can happily save the object itself as long as it needs).
- easier for multithreaded programs to be correct?
- the semantics match more closely (the “builder” pattern has a definitive point when it’s done with everything).
Our real code uses a done() method to ensure that you can’t call more builder-type methods after you have concluded.
This is a prefect place to also “un-consume” out the previously-consumed Write instance (by returning it).
Conclusion and Questions
This distilled example doesn’t illustrate the “lifetime” stuff required to make the “mutable reference” version work, but it does get at the essence.
Do other Rust programmers have guidance? Are either of these typical patters? Do they have names?
Respond in this Mastodon thread.
txtorcon
carml
cuv’ner