I'm trying to write some code that will generate a random struct with a random value. I have the following trait and helper macros for the structs:
use rand::{thread_rng, Rng};
use std::fmt;
pub trait DataType {
/// generate a new instance of the type with a random value
fn random() -> Box<Self>;
/// generate a new instance of the same type with a random value
fn gen_another(&self) -> Box<Self>;
}
macro_rules! impl_data_type_for_num {
($x:ident) => {
impl DataType for $x {
fn random() -> Box<Self> {
Box::new(Self {
value: thread_rng().gen()
})
}
fn gen_another(&self) -> Box<Self> {
Self::random()
}
}
};
}
macro_rules! impl_formatting {
($x:ident, $s:expr) => {
impl fmt::Debug for $x {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, $s)
}
}
impl fmt::Display for $x {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
};
}
Then I use the macros to implement the needed traits on a bunch of structs (heres a few for example):
pub struct VirtBool {
value: bool
}
impl_data_type_for_num!(VirtBool);
impl_formatting!(VirtBool, "bool");
pub struct VirtU8 {
value: u8
}
impl_data_type_for_num!(VirtU8);
impl_formatting!(VirtU8, "u8");
pub struct VirtU16 {
value: u16
}
impl_data_type_for_num!(VirtU16);
impl_formatting!(VirtU16, "u16");
So far it all works fine, but then an issue arises when I try to implement the same traits on a struct with unsized fields:
pub struct VirtArray {
_type: Box<dyn DataType>,
value: Vec<Box<dyn DataType>>
}
impl DataType for VirtArray {
fn random() -> Box<Self> {
let t = random_var();
let s = thread_rng().gen_range(0, 10);
Box::new(Self {
_type: *t,
value: (0..s).map(|_| t.gen_another()).collect()
})
}
fn gen_another(&self) -> Box<Self> {
Box::new(Self {
_type: self._type,
value: self.value.iter().map(|t| t.gen_another()).collect::<Vec<Box<dyn DataType>>>()
})
}
}
impl fmt::Debug for VirtArray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{:?}; {}]", self._type, self.value.len())
}
}
impl fmt::Display for VirtArray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = self.value.iter().map(|v| format!("{}, ",v)).collect::<String>();
s.truncate(s.len().checked_sub(2).unwrap_or(s.len()));
write!(f, "[{}]", s)
}
}
This throws the error the trait 'DataType' cannot be made into an object on several occasions. I then have a function to create a random struct but that also throws the same error:
/// generate a random type with random value
fn random_var() -> Box<dyn DataType> {
match thread_rng().gen_range(0,4) {
0 => Box::new(VirtBool::random()),
1 => Box::new(VirtU8::random()),
2 => Box::new(VirtU16::random()),
3 => Box::new(VirtArray::random()),
_ => panic!("invalid")
}
}
I was originally using enums to do all of this but I'm trying to switch it over to structs and traits to help with scaling/use-ability. Does anyone have any idea how to fix the code above? I've been at a roadblock here for quite a while now.
Also, I'm aware I could use type_name_of_val to print the types, but I'm trying to keep from using unstable/nightly features.
DataType cannot be made into a trait object because it uses Self and because it has a static method.
I realize it might seem like returning Box<Self> may be reasonable to call on a dyn DataType, since if you call it on dyn DataType you want a Box<dyn DataType>, but Rust doesn't try to modify methods for you to turn methods that return e.g. Box<VirtArray> into ones that return Box<dyn DataType> if they are called on a dyn DataType value. You can work around this by having the methods return Box<dyn DataType> instead.
Static methods are not allowed for trait objects because there is no implementation for the trait object type. Remember, dyn Foo implements Foo. What would (dyn DataType)::random() be? (You can use a where clause, as in the example above, to make sure dyn DataType isn't expected to have this method in a way that can be detected ahead of time; this means you can't use it on your dyn DataType objects, but it sounds like you might not want to.)
Related
I'm implementing the Debug trait on a trait. I'd like to be able to display the name of the concrete type that implements this particular instance of the trait.
trait Node {
foo: String,
}
impl fmt::Debug for dyn Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = // get type name of self here
write!(f, "The name: {}", name);
}
}
I've read a number of posts about Any and downcasting and whatnot, but these solutions seem complex.
One possible solution is to add a method to the trait itself: get_name() -> String, and implement it for each struct individually. There has to be a simpler way, though.
We could add a get_name method to Node which returns whatever the name of Self is using std::any::type_name::<Self>().
trait Node {
fn get_name(&self) -> &'static str {
return std::any::type_name::<Self>();
}
}
Inside the Debug implementation we can call self.get_name().
impl fmt::Debug for dyn Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = self.get_name();
write!(f, "The name: {}", name)
}
}
Printing a dyn Node instance will show the following result:
struct SomeStruct {}
impl Node for SomeStruct {}
fn main() {
let a: Box<dyn Node> = Box::new(SomeStruct {});
println!("{:?}", a)
// The name: playground::SomeStruct
}
Playground
I want to simplify files and indention levels for implementing several Traits for a given struct. Is there a way I can do something like this?
struct Foo { /* ommitted */ }
impl(Default(Foo)) fn default() -> Self {
/* do something */
}
Obviously, it is possible to write
impl Default for Foo {
fn default() -> Self {
/* do something */
}
}
but the enclosing text provides no more value than the macro I hope exists. Although I am new to Rust, I am well aware of (and frequently use)
#[derive(Default)]
but I am looking for a way to simplify actually implementing many traits. I would consider the following clear as well, but AFAIK it is not legal Rust. Note that the following is similar to rust-lang #1250:
impl Foo {
type Index::Output = Bar;
trait Default fn default() -> Self {
// ...
}
trait Debug fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
}
trait Display fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// ...
}
trait Index<usize> fn index(&self, (r, c): (usize, usize)) -> &Self::Index::Output {
// ...
}
fn my_custom_thing() -> () {
// ...
}
}
Whenever I tried something like this, I found that it was easier to just write out the impls.
Thus, I would advise against it. But if you insist, you could start out with the following:
macro_rules! quick_impl {
($structname: ident, Default, $($t:tt)*) => {
impl Default for $structname {
fn default() -> Self {
Self {$($t)*}
}
}
};
($structname: ident, Debug, $($field:ident),*) => {
impl std::fmt::Debug for $structname {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.debug_struct(stringify!($tyname))
$(
.field(stringify!($field), &self.$field)
)*
.finish()
}
}
};
// here be more implementations
}
Usable like this:
struct S {
a: usize,
b: bool,
c: char,
}
quick_impl!(S, Default, a:1, b:true, c:'c');
quick_impl!(S, Debug, a, b, c);
This macro goes a step further in that you do not have to say trait or fn in the arguments (as this is clear anyway).
I have some code which returns a trait object of type MyTrait so that it can return one of several different structs. I would like to implement the Display trait for the trait object so that I can print the object, with the details delegated to the various structs as they each need their own custom formatters.
I can achieve this by including a formatting method as part of the MyTrait definition, and then implementing Display for MyTrait and delegating - like this:
trait MyTrait {
fn is_even(&self) -> bool;
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
impl fmt::Display for MyTrait {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.my_fmt(f)
}
}
However, I already have the Display trait implemented for each of the structs which implement MyTrait. This means I end up with two methods for each struct which do the same thing - the fmt() method to satisfy the Display trait directly on the struct, and the my_fmt() method which is called by the code above. This seems clumsy and repetitive. Is there a simpler way to do it?
Here's a complete example program which illustrates the point. It's a little longer than I would have liked (it's based on the answer to my previous question Calling functions which return different types with shared trait and pass to other functions), but I couldn't think of a simpler way to illustrate the point. Of course, in this toy example the structs and the fmt functions are very simple; in my real application they are more complex.
use std::fmt;
trait MyTrait {
fn is_even(&self) -> bool;
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result;
}
struct First {
v: u8,
}
struct Second {
v: Vec<u8>,
}
impl MyTrait for First {
fn is_even(&self) -> bool {
self.v % 2 == 0
}
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
}
}
impl MyTrait for Second {
fn is_even(&self) -> bool {
self.v[0] % 2 == 0
}
fn my_fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v[0])
}
}
fn make1() -> First {
First { v: 5 }
}
fn make2() -> Second {
Second { v: vec![2, 3, 5] }
}
// Implement Display for the structs and for MyTrait
impl fmt::Display for First {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v)
}
}
impl fmt::Display for Second {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.v[0])
}
}
impl fmt::Display for MyTrait {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.my_fmt(f)
}
}
fn requires_mytrait<T: MyTrait + ?Sized>(v: &&T) {
println!("{:?}", v.is_even());
}
fn main() {
for i in 0..2 {
let v1;
let v2;
let v = match i {
0 => {
v1 = make1();
println!("> {}", v1); // Demonstrate that Display
// is implemented directly
// on the type.
&v1 as &MyTrait
}
_ => {
v2 = make2();
println!("> {}", v2); // Demonstrate that Display
// is implemented directly
// on the type.
&v2 as &MyTrait
}
};
requires_mytrait(&v);
println!("{}", v); // Here I print the trait object
}
}
Can anyone suggest a simpler, cleaner way to do this?
You can make Display a supertrait of MyTrait.
trait MyTrait: fmt::Display {
fn is_even(&self) -> bool;
}
This will make trait objects of MyTrait be Display. This only works if you expect all implementors of MyTrait to implement Display, but that was also the case in your previous solution.
I have latitude in degrees in the form of a f64 and I need to convert it into a String. At first I thought about implementing Display, like this:
struct Latitude(f64);
impl fmt::Display for Latitude {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", if self.0 > 0. { "N" } else { "S" }, self.0)
}
}
fn main() {
let lat: f64 = 45.;
println!("{}", Latitude(lat));
}
After that, I have additional requirements. I need convert to one of two representations:
N 70.152351
N 70° 09' 08"
There is also an additional flag; when it is false, I need something like:
- --.------
- --° -' -"
The most simple way to implement this will be:
fn format_lat(degree: f64, use_min_sec_variant: bool, is_valid: bool) -> String;
However, I don't see any free functions in the Rust standard library.
Maybe I should use struct Latitude(f64) and implement a to_string method? Or maybe I should implement some other trait?
You can create wrapper types with different types of formatting and return them as a method:
struct Latitude(f64);
struct MinutesSeconds(f64);
impl Latitude {
fn minutes_seconds(&self) -> MinutesSeconds {
MinutesSeconds(self.0)
}
}
impl fmt::Display for MinutesSeconds {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "latitude in a different format")
}
}
This is the same idea as Path::display.
As for the valid / invalid concept, it sounds like you really want a struct Latitude(Option<f64>) and then provide None when it is invalid.
Basically you're free to do what you want. Without more context on your goals, any method seems good enough for me.
Personally I'd do it as:
struct Latitude(f64);
impl Latitutde {
pub fn format_as_x(&self, f: &mut fmt::Formatter) -> fmt::Result<(), Error> {}
pub fn format_as_y(&self, f: &mut fmt::Formatter) -> fmt::Result<(), Error> {}
}
plus a Display trait + to_format_x() -> String convenience functions.
format_as_y are IMO best method since they handle errors, can take any formatter and don't need to allocate a String to return.
By the way, taking bool arguments is usually an anti-pattern: boolean trap.
You can impl Latitude if you need to pass arguments or impl ToString for Latitude if you don't:
use std::string::ToString;
struct Latitude(f64);
// If you need parameterisation on your conversion function
impl Latitude {
// You'd probably use a better-suited name
pub fn to_string_parameterised(&self, use_min_sec_variant: bool) -> String {
// Conversion code
format!("{} {}", self.0, use_min_sec_variant)
}
}
// If you don't need parameterisation and want to play nicely with 100% of all code elsewhere
impl ToString for Latitude {
fn to_string(&self) -> String {
// Conversion code
format!("{}", self.0)
}
}
fn main() {
let lat = Latitude(45.0);
println!("{}", lat.to_string_parameterised(false));
println!("{}", lat.to_string());
}
I am trying to print an Option<Box<MyStruct>>, but I get a compile error when trying to implement Display for Option<Box<MyStruct>>.
use std::fmt;
fn main() {
let maybe_my_struct: Option<Box<MyStruct>> = Some(Box::new(MyStruct{foo:42}));
println!("{}", maybe_my_struct);
}
struct MyStruct {
foo: i32,
}
impl fmt::Display for Option<Box<MyStruct>> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
Some(MyStruct) => write!(formatter, "{}", self.foo),
None => write!(formatter, "No struct"),
}
}
}
The error I get is :
error: the impl does not reference any types defined in this crate;
only traits defined in the current crate can be implemented for arbitrary types [E0117]
I have tried aliasing the Option type, and instead implementing Display for MyOption<Box<MyStruct>>, but that gives the same result. What am I doing wrong?
As you can see, you can't implement a trait you didn't write for a type you didn't write. This is part of what's known as "coherence" and exists to prevent really weird things like linking against a library suddenly causing unrelated parts of your program to change behaviour.
Aliasing Option to MyOption doesn't work either because, as you say, it's an alias. That is, it's just another name for the same thing, it's not an actual, different type.
Now, if you write a wrapper around Option like so:
struct MyOption<T>(Option<T>);
then MyOption will be a new, distinct type that you can implement a trait for. Of course, you'll want to write methods to wrap and unwrap the actual Option you're storing.
... But this is all rather irrelevant since you could also just derive Debug for your struct and use that.
fn main() {
let maybe_my_struct: Option<Box<MyStruct>> = Some(Box::new(MyStruct{foo:42}));
println!("{:?}", Some(maybe_my_struct));
}
#[derive(Debug)]
struct MyStruct {
foo: i32,
}
Or, if you really want the custom display logic for the Option<Box<MyStruct>> combination, you can use a marker value (this same approach is used by Path in the standard library, incidentally). Like so:
use std::fmt;
fn main() {
let maybe_my_struct: Option<Box<MyStruct>> = Some(Box::new(MyStruct{foo:42}));
println!("{:?}", maybe_my_struct);
// Instead of displaying directly, display via a custom marker.
println!("{}", maybe_my_struct.display());
println!("{}", None::<Box<MyStruct>>.display());
}
#[derive(Debug)]
struct MyStruct {
foo: i32,
}
// This is the marker we'll use to define our custom Display impl.
struct MmsDisplay<'a>(&'a Option<Box<MyStruct>>);
// This trait lets us extend Option<Box<MyStruct>> with a new method.
trait CustomMmsDisplay {
fn display<'a>(&'a self) -> MmsDisplay<'a>;
}
impl CustomMmsDisplay for Option<Box<MyStruct>> {
fn display<'a>(&'a self) -> MmsDisplay<'a> {
MmsDisplay(self)
}
}
// And here's the display logic.
impl<'a> fmt::Display for MmsDisplay<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match *self.0 {
Some(ref ms) => write!(formatter, "{}", ms.foo),
None => write!(formatter, "No struct"),
}
}
}