#Storing any object that you can Write to

31 messages · Page 1 of 1 (latest)

lunar rune
#

Originally, my struct only printed to stdout using println!. However, I now have the need for it to be able to print output to other places, such as Strings. How would I go about doing this?

In C++ for example, I'd have the struct store a reference to an ostream, because both strings and stdout inherit from it.
In Rust though, this is a little harder, particularly because of borrow checking.
I initially thought that I could store the target output using Box<dyn Write>, which I could then write to with writeln!. This works fine when I use std::io::stdout() as the output. But, when I try to put a writer for a Vec<u8>, it's problematic because I won't be able to read the characters later, because the struct now owns the writer.
How can I do this?

brittle canyon
#

depends on the nature of the “later”

#

for example, if there's an enclosing scope, you can hand the struct a &mut Vec<u8> (which does implement Write, so it will fit in a Box<dyn Write + 'a>) and then afterward, the Vec is available to read

#

another possibility is that the struct should (1) be generic over the writer instead of using dyn, and (2) have a method which dismantles it and returns the writer (this is a common pattern, called something like into_inner())

#

since it's generic rather than dyn, the caller can retrieve the inner as its original type

#

a third option might be to build a custom type that implements Write to write to internal shared (via a mutex) storage

#

if you can post some sample code of how you want it to be used I can recommend more

lunar rune
#

ahh, actually I think generics might be a good solution for this

#

really, I'd imagine most users won't use their own writer and just leave it as is for printing to stdout

#

what I'm working on is a VM for a scripting language

#

by default, its print function will print to stdout, but for the purposes of unit testing (and other use cases people might have) I want to have the option for print to output to anywhere the user wishes

#

i.e. a rust string

brittle canyon
#

the downside of generics is that then the generic parameter has to be passed along everywhere that contains the generic struct

#

which might be bad if it's all the pieces of the VM

#

generics are great if they fit your code structure well though

#

in that situation I might actually take the 'custom type that implements Write' option

#

sort of making a 'pipe' but internal to the code

lunar rune
brittle canyon
#
struct OutputCapturer {
    storage: Arc<Mutex<Vec<u8>>>,
}
impl Write for OutputCapturer {
    fn write(&mut self...) {
        storage.lock().unwrap().extend(buf)
    }

roughly that sort of thing

#

then somebody else can read it at any time by holding a clone of the Arc

lunar rune
#

ah, I've never used any of the thread-related types before (Arc, Mutex and others), I guess I have to research more about those...

brittle canyon
#

the single-threaded-only equivalent is Rc<RefCell<

#

I just use Arc reflexively because using Rc instead is condemning the type to be used strictly single threaded

lunar rune
brittle canyon
#

it doesn't by itself; it's just something that you can put in your Box<dyn Write>

#

it solves the "won't be able to read the characters later" problem

lunar rune
#

hmm okay, so I've implemented what you wrote so far, now for the final challenge... how do I actually read from OutputCapturer after it's owned by the struct?

#

nvm figured it out, it seems Cloneing them does it

#

nice that actually works!!! thanks

#

although I still don't fully understand what Mutex is (or Cell for that matter)

brittle canyon
#

Mutex (or RefCell) expresses the idea of "two different callers can modify the contents of this, or look at the results of that modification"