-
C
's FILE *
would be Rust’s File
, although it is less versatile.
-
So you need something like “an object with virtual methods”. In Rust, this can be achieved by erasing a concrete type (e.g., Stdout
, Stderr
, the aforementioned File
) into a type describing the common behavior (all three implement the trait io::Write
), that is, the trait object type: dyn io::Write
.
In order to perform dynamic dispatch, the trait object needs to be behind some form of indirection. To store it without borrowing it, it requires Box
ing the element into the heap: your struct could thus then hold a Box<dyn io::Write>
.
If you just need it as a parameter to a function, such as with your dump_info
example, then you can make the function generic over all the types that are Write
-able:
#![deny(bare_trait_objects)]
use ::std::io::{self, Write};
impl MyFoo {
/// function can be fed a `&mut Stdout`, a `&mut File`, and also a `&mut dyn Write`
fn dump_info (out: &mut impl Write + ?Sized)
{
out.write("blah, blah, blah");
}
}
or, for slightly faster compile times and slighlty smaller binaries, you can just use one monomorphization of the above generic function: the case using dyamic dispatch (since writing is an expensive operation anyways, this is one case where the virtual method cost is not a problem. But for other traits, prefer the generic approach whenever possible):
#![deny(bare_trait_objects)]
use ::std::io::{self, Write};
impl MyFoo {
/// function can only be fed a `&mut dyn Write`,
/// but luckily `&mut Stdout`, `&mut File`, etc. coerce to `&mut dyn Write`
fn dump_info (out: &mut dyn Write)
{
out.write("blah, blah, blah");
}
}
- which is @leudz’s example (except they have used the deprecated trait object notation without
dyn
).