There is a way to define a common trait for String and stdout?

In my little project all I need is to write either to a String or the stdout. How can I accomplish this without creating a version of if for each type? I was thinking in implementing a trait Writable that would do the trick but now it is poping alot of errors related of the trait must be dynamic and the size of object cannot be known at compile time:

Here is the trait:

trait Writable {
    fn write_str(self, str: &str);
    fn write_char(self, c: char);
}

impl Writable for String {
    fn write_str(self, str: &str) {
        self.write_str(str)
    }

    fn write_char(self, c: char) {
        self.write_char(c)
    }
}

impl Writable for Stdout {
    fn write_str(self, str: &str) {
        self.write_str(str)
    }

    fn write_char(self, c: char) {
        self.write_char(c)
    }
}

Here is the error that i get from rust anlyzer:

Does the Write trait not do what you want, already? Write in std::io - Rust

You could use the Cursor struct for the in-memory field (I think you can convert it to a String).

2 Likes

And actually, according to Shep on Stack Overflow, you don't even need a Cursor--a Vec<u8> will work just fine, as long as you don't need to seek: How to create an in-memory object that can be used as a Reader, Writer, or Seek in Rust? - Stack Overflow

1 Like

tried that write in std::io but I was getting the same errors that i pointed below and i dont know if I can pass a String to it. Maybe if I can figure out whats going on down there maybe I can use it

Trait objects need to be behind some kind of indirection like a reference or pointer.

Something like output: &mut dyn Write should work

2 Likes

Ah, my apologies--I looked at the trait implementation and didn't look at the image.

I think what you want is a reference:

pub fn sort_path_length(input: &str, invert: bool, output: &mut dyn Write) -> ! {

edit: or what semicoleon said

1 Like

Yes, that works for the stdout reference (thanks btw), but if I want to pass a String in the same manner like stdin::read_to_string()?

The Playground above contains a complete code example that shows you exactly how to obtain a string: pass a &mut Vec<u8> which implements io::Write, and then convert it to a string or string slice.

You can't pass a String because it does not implement io::Write. (It implements fmt::Write but that doesn't help, because standard streams aren't fmt::Write, so there's no common trait. You could define your own, though.)

1 Like

Oh, sorry, didn´t saw that playground. Thanks a lot for clarification, I have everything I need to know now

Hrm....it seems like you can't pass in a String directly. But as in my playground, you can pass in a Vec<u8>, and you can convert between that and String.

The one catch is that you cannot convert a String to a Vec<u8> and keep the original String around[1]. You're better off IMO using a Vec<u8> as your underlying storage, if you can, and then just converting it to a String when you need a String.


  1. well you can, but it requires unsafe code ↩︎

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.