Issues with Boxed trait objects

I am having some issues with trait objects and Box, and don't understand how it all works well enough to troubleshoot.

I have the following code (simplified), and whenever I call methods on an element of the Vec, I get method not found, or trait bound issues.


use egui::widget::Widget

// Just intended to be a marker trait.
trait SchematicConnector: Widget {}

struct RightAngle {
//implementation omitted.
}

impl Widget for &mut RightAngle {
//implementation omitted.
}

impl SchematicConnector for RightAngle {}

struct MultiRightAngle {
    inner_connectors: Vec<Box<dyn SchematicConnector>>,
//implementation omitted.
}

impl Widget for &mut MultiRightAngle {
//implementation omitted.
}

impl SchematicConnector for MultiRightAngle {}


let connections: Vec<Box<dyn SchematicConnector>> = Vec::new();

connections.push(Box::new(RightAngle{}));
connections.push(Box::new(MultiRightAngle{
// populate inner vec
}));

for connection in connections {
    // do something if connection type = RightAngle
    // do something if connection type = MultiRightAngle

    // call method from Widget impl on either type
}

I tried to do this with Any and kept running into issues with unimplemented traits and poor error messages.

I also attempted to create an enum and match on that, however that resulted in duplicating the code that is common for both RightAngle and MultiRightAngle

enum ConnectorType {
    RightAngle(RightAngle),
    MultiRightAngle(MultiRightAngle),
}

If I attempt to call ui.place(connection) for example, I have to unwrap the enum to get the inner value, and then do that as many times as I have enum variants, even though they all impl Widget.

The enum solution "works", but is very fiddly. I could not get the Any solution to work at all, even though it seems like it is the more "rusty?" somehow.

Any advice or suggestions would be appreciated!

please show the error messages if possible.

since the code snippet is reduced, it's hard to deduce what you are trying to achieve and where the errors are.

again, you should show the error messages.

if you want to downcasting a trait object, the Any trait must be a super trait of the trait object.

does it work if you implement Widget for the enum, which delegates to the variants?

impl Widget for ConnectorType {
    fn ui(self, ui: &mut Ui) -> Response {
        match self {
            ConnectorType::RightAngle(widget) => widget.ui(ui),
            ConnectorType::MultiRightAngle(widget) => widget.ui(ui),
        }
    }
}

because Widget is a foreign trait, crates like enum-dispatch won't help in this case, you need to manually write the dispatching. luckily, the Widget trait is rather simple.

it's not "more rusty", it's just a different approach with different trade-offs.

in fact, in your example where the different types of connectors are known in prior, I would say the enum based solution is more idiomatic.

If you want to use dyn, you have to choose or design a single trait which has all operations you want to have available, either as its own methods or supertraits. In particular:

  • If you want to make use of Any, you need it as a supertrait:

    trait SchematicConnector: Any {...}
    

    Adding the Any supertrait means that you can coerce a &[mut] dyn SchematicConnector to &[mut] dyn Any, and then use Any's downcasting.

  • Since Widget is supposed to be implemented for &muts or similar, trait SchematicConnector: Widget can't help you implement Widget in the way you need. Instead, you need to define your own method, and delegate to it:

    trait SchematicConnector: Any {
        fn ui(&mut self, ui: &mut Ui) -> Response; // our own method like Widget::ui()
    }
    
    impl Widget for &mut Box<dyn SchematicConnector> {
        fn ui(self, ui: &mut Ui) -> Response {
            SchematicConnector::ui(&mut **self, ui)
        }
    }
    

This is all with the dyn impl. I redid it to capture the errors. I am interested in learning more about this, so happy to try stuff out here.

The first error message I get, is on my impl SchematicConnector for MultiRightAngle {}

The trait bound MultiRightAngle: Widget is not satisfied, unsatisfied trait bound.

Where SchematicConnector is defined as

pub trait SchematicConnector: Widget + Any {}

I have a functioning impl Widget for &mut MultiRightAngle as the next line below the SchematicConnector impl. Not sure what is breaking there.

The error messages I get in the for loops, looping over connectors and running methods on them are:

no method named move_end1_position found for mutable reference &mut Box<(dyn SchematicConnector + 'static)> in the current scope: method not found in &mut Box<(dyn SchematicConnector + 'static)>

If I change the for loop to the below, for testing, I get the errors:

no method named &mut Box<(dyn SchematicConnector + 'static)> in the current scope

and this one on ra inside the match branch.

type annotations needed: cannot infer type

for connection in &mut self.end1_connections {
    match connection.downcast_mut::<RightAngle>() {
        Some(ra) => {
            ra.move_end1_position(drag_delta);
        }
        None => {
            todo!()
        }
    }
}

Based on the last error there, it seems like Any is not a supertrait of SchematicConnector somehow. Do I have to implement Any manually on everything that also implements SchematicConnector?

does it work if you implement Widget for the enum, which delegates to the variants?

Is there no way of having this automatically dereference, given that the trait is implemented for both inner values of the enum variants?

I can try, but not sure how that would work exactly.

I had SchematicConnector defined as trait SchematicConnector: Any + Widget {} during my testing, but that didn't seem to help any. See my response to nerditation.

I am aware that I need to impl Widget separately for everything. I just want anything that implements SchematicConnector to also implement Widget.

This is the first time I have messed around with Trait objects, boxes and dyn so there is probably something pretty obvious I am missing here.

You should ideally get the full error messages. If it's too hard to find in your IDE maybe use cargo check in the terminal. Every error message is not just one line, but a whole section, with ascii art arrows or underlines into code locations and possibly additional notes, and helps us a lot to better understand what actual problem you're facing when communicated in a forum help thread :wink:


That being said, I can make some guesses already what some of the errors may relate to.

For example, it looks like you're implementing SchematicConnector for the type MultiRightAngle whereas you've shown us an implementation of Widget on &mut MultiRightAngle. The problem is that MultiRightAngle and &mut MultiRightAngle are two different types and thus the compiler doesn't accept the presence of a Widget impl on the &mut ... type to fulfill the subtrait-supertrait relation you've declared.

That's a difficulty with the usage pattern that egui suggested here, @kpreid already gave some suggestions above on how to resolve this.

I assume the issue is that you'll have to add some step of turning &mut Box<dyn SchematicConnector> into &mut dyn Any before the downcast. Let me test the needed syntax... I'll edit shortly.

Edit1: Okay… I got to the point of sketching the API surface, implementing a solution like @kpreid suggested (actually introducing an additional helper trait, so your impls can truly look just as simple as

impl egui::Widget for &mut RightAngle {
    fn ui(self, ui: &mut egui::Ui) -> egui::Response {
        todo!()
    }
}

impl SchematicConnector for RightAngle {} // still no items, yay!

and reproduced the remaining errors around the downcast in this playground.

error[E0599]: no method named `downcast_mut` found for mutable reference `&mut Box<(dyn SchematicConnector + 'static)>` in the current scope
  --> src/lib.rs:52:26
   |
52 |         match connection.downcast_mut::<RightAngle>() {
   |                          ^^^^^^^^^^^^
   |
help: there is a method `as_mut` with a similar name
   |
52 -         match connection.downcast_mut::<RightAngle>() {
52 +         match connection.as_mut::<RightAngle>() {
   |

error[E0282]: type annotations needed
  --> src/lib.rs:54:17
   |
54 |                 ra.move_end1_position(drag_delta);
   |                 ^^ cannot infer type

^^^^ this is how properly shared error messages look like :wink:

Edit2: So this can work now

match <dyn Any>::downcast_mut::<RightAngle>(&mut **connection) {
    Some(ra) => {
        ra.move_end1_position(drag_delta);
    }
    None => {
        todo!()
    }
}

(playground)

but the syntax is annoyingly ugly and also without the &mut ** dance it still compiles but doesn’t downcast successfully (because it then creates a &mut dyn Any by means of observing the type of Box<dyn SchematicConnector> (any &mut T can be turned into &mut dyn Any if T: 'static is fulfilled, and here, T = Box<dyn SchematicConnector> could be used) instead of using &mut dyn SchematicConnector and then using trait upcasting to access the Any supertrait.

I suppose this can be made nicer by adding a helper method instead. Many possible approaches, perhaps a stupidly simple one could be as little as

impl dyn SchematicConnector {
    fn as_any_mut(&mut self) -> &mut dyn Any {
        self 
    }
}

(with all the types from the signature here you can just have the casting happen implicitly and just write self here, yay :sweat_smile:)

and then something like

match connection.as_any_mut().downcast_mut::<RightAngle>() {
    Some(ra) => {
        ra.move_end1_position(drag_delta);
    }
    None => {
        todo!()
    }
}

works nicely :slight_smile:

MultiRightAngle and &mut MultiRightAngle are different types.

you may choose to implement the SchematicConnector trait for either one, or both, depending on your use case. which ever type you implement the trait for, you must also implement the super trait, i.e. Widget, otherwise, you get the first compilation error.

this is an imcomplete error message which doesn't make much sense, please post the original compiler ouptut verbatim.

this is the complete output of cargo clippy. I was trying to not throw a ton of text at folks, as some of the output from clippy can be a bit verbose.

As this code is still in active development, there are a lot of errrors that are unrelated.

I was posting the last response late at night and didn't fully fill out the last error message. It was supposed to be:

no method named downcast_mut found for mutable reference &mut Box<(dyn SchematicConnector + 'static)> in the current scope

Sorry about that.

    Checking connection_diagram_manager v0.0.1 (/home/toxicsauce/projects/src/github.com/sww1235/connection-diagram-manager/crates/cdm_core)
warning: unused import: `log::trace`
  --> crates/cdm_core/src/datatypes/schematic_symbol.rs:15:5
   |
15 | use log::trace;
   |     ^^^^^^^^^^
   |
   = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default

error[E0277]: the trait bound `datatypes::schematic_connector::multi_right_angle::MultiRightAngle: egui::Widget` is not satisfied
  --> crates/cdm_core/src/datatypes/project_types/cable.rs:74:19
   |
74 |     type Output = MultiRightAngle;
   |                   ^^^^^^^^^^^^^^^ unsatisfied trait bound
   |
help: the trait `for<'a> FnOnce(&'a mut egui::Ui)` is not implemented for `datatypes::schematic_connector::multi_right_angle::MultiRightAngle`
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:21:1
   |
21 | pub struct MultiRightAngle {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the following other types implement trait `datatypes::schematic_connector::SchematicConnector`
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:44:1
   |
44 | impl SchematicConnector for MultiRightAngle {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `datatypes::schematic_connector::multi_right_angle::MultiRightAngle`
   |
  ::: crates/cdm_core/src/datatypes/schematic_connector/right_angle.rs:43:1
   |
43 | impl SchematicConnector for RightAngle {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `datatypes::schematic_connector::right_angle::RightAngle`
   = note: required for `datatypes::schematic_connector::multi_right_angle::MultiRightAngle` to implement `egui::Widget`
note: required for `<datatypes::project_types::cable::Cable as datatypes::schematic_connector::AsConnector>::Output` to implement `datatypes::schematic_connector::SchematicConnector`
  --> crates/cdm_core/src/datatypes/schematic_connector.rs:55:11
   |
55 | pub trait SchematicConnector: Widget + Any {}
   |           ^^^^^^^^^^^^^^^^^^
note: required by a bound in `datatypes::schematic_connector::AsConnector::Output`
  --> crates/cdm_core/src/datatypes/schematic_connector.rs:33:18
   |
33 |     type Output: SchematicConnector;
   |                  ^^^^^^^^^^^^^^^^^^ required by this bound in `AsConnector::Output`

error[E0277]: the trait bound `datatypes::schematic_connector::right_angle::RightAngle: egui::Widget` is not satisfied
  --> crates/cdm_core/src/datatypes/project_types/wire.rs:60:19
   |
60 |     type Output = RightAngle;
   |                   ^^^^^^^^^^ unsatisfied trait bound
   |
help: the trait `for<'a> FnOnce(&'a mut egui::Ui)` is not implemented for `datatypes::schematic_connector::right_angle::RightAngle`
  --> crates/cdm_core/src/datatypes/schematic_connector/right_angle.rs:28:1
   |
28 | pub struct RightAngle {
   | ^^^^^^^^^^^^^^^^^^^^^
help: the following other types implement trait `datatypes::schematic_connector::SchematicConnector`
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:44:1
   |
44 | impl SchematicConnector for MultiRightAngle {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `datatypes::schematic_connector::multi_right_angle::MultiRightAngle`
   |
  ::: crates/cdm_core/src/datatypes/schematic_connector/right_angle.rs:43:1
   |
43 | impl SchematicConnector for RightAngle {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `datatypes::schematic_connector::right_angle::RightAngle`
   = note: required for `datatypes::schematic_connector::right_angle::RightAngle` to implement `egui::Widget`
note: required for `<datatypes::project_types::wire::Wire as datatypes::schematic_connector::AsConnector>::Output` to implement `datatypes::schematic_connector::SchematicConnector`
  --> crates/cdm_core/src/datatypes/schematic_connector.rs:55:11
   |
55 | pub trait SchematicConnector: Widget + Any {}
   |           ^^^^^^^^^^^^^^^^^^
note: required by a bound in `datatypes::schematic_connector::AsConnector::Output`
  --> crates/cdm_core/src/datatypes/schematic_connector.rs:33:18
   |
33 |     type Output: SchematicConnector;
   |                  ^^^^^^^^^^^^^^^^^^ required by this bound in `AsConnector::Output`

error[E0277]: the trait bound `datatypes::schematic_connector::multi_right_angle::MultiRightAngle: egui::Widget` is not satisfied
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:44:29
   |
44 | impl SchematicConnector for MultiRightAngle {}
   |                             ^^^^^^^^^^^^^^^ unsatisfied trait bound
   |
help: the trait `for<'a> FnOnce(&'a mut egui::Ui)` is not implemented for `datatypes::schematic_connector::multi_right_angle::MultiRightAngle`
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:21:1
   |
21 | pub struct MultiRightAngle {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the trait `egui::Widget` is implemented for `&mut datatypes::schematic_connector::multi_right_angle::MultiRightAngle`
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:46:1
   |
46 | impl Widget for &mut MultiRightAngle {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: required for `datatypes::schematic_connector::multi_right_angle::MultiRightAngle` to implement `egui::Widget`
note: required by a bound in `datatypes::schematic_connector::SchematicConnector`
  --> crates/cdm_core/src/datatypes/schematic_connector.rs:55:31
   |
55 | pub trait SchematicConnector: Widget + Any {}
   |                               ^^^^^^ required by this bound in `SchematicConnector`

error[E0277]: the trait bound `datatypes::schematic_connector::right_angle::RightAngle: egui::Widget` is not satisfied
  --> crates/cdm_core/src/datatypes/schematic_connector/right_angle.rs:43:29
   |
43 | impl SchematicConnector for RightAngle {}
   |                             ^^^^^^^^^^ unsatisfied trait bound
   |
help: the trait `for<'a> FnOnce(&'a mut egui::Ui)` is not implemented for `datatypes::schematic_connector::right_angle::RightAngle`
  --> crates/cdm_core/src/datatypes/schematic_connector/right_angle.rs:28:1
   |
28 | pub struct RightAngle {
   | ^^^^^^^^^^^^^^^^^^^^^
help: the trait `egui::Widget` is implemented for `&mut datatypes::schematic_connector::right_angle::RightAngle`
  --> crates/cdm_core/src/datatypes/schematic_connector/right_angle.rs:45:1
   |
45 | impl Widget for &mut RightAngle {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: required for `datatypes::schematic_connector::right_angle::RightAngle` to implement `egui::Widget`
note: required by a bound in `datatypes::schematic_connector::SchematicConnector`
  --> crates/cdm_core/src/datatypes/schematic_connector.rs:55:31
   |
55 | pub trait SchematicConnector: Widget + Any {}
   |                               ^^^^^^ required by this bound in `SchematicConnector`

error[E0308]: arguments to this function are incorrect
   --> crates/cdm_core/src/datatypes/project_types/cable.rs:105:12
    |
105 |         Ok(MultiRightAngle::new(
    |            ^^^^^^^^^^^^^^^^^^^^
    |
note: expected `Vec<Box<dyn SchematicConnector>>`, found `Vec<RightAngle>`
   --> crates/cdm_core/src/datatypes/project_types/cable.rs:107:13
    |
107 |             end1_connections,
    |             ^^^^^^^^^^^^^^^^
    = note: expected struct `std::vec::Vec<std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>>`
               found struct `std::vec::Vec<datatypes::schematic_connector::right_angle::RightAngle>`
note: expected `Vec<Box<dyn SchematicConnector>>`, found `Vec<RightAngle>`
   --> crates/cdm_core/src/datatypes/project_types/cable.rs:109:13
    |
109 |             end2_connections,
    |             ^^^^^^^^^^^^^^^^
    = note: expected struct `std::vec::Vec<std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>>`
               found struct `std::vec::Vec<datatypes::schematic_connector::right_angle::RightAngle>`
note: associated function defined here
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:184:12
    |
184 |     pub fn new(
    |            ^^^
185 |         end1_junction: ConnectionPoint,
186 |         end1_connections: Vec<Box<dyn SchematicConnector>>,
    |         --------------------------------------------------
187 |         end2_junction: ConnectionPoint,
188 |         end2_connections: Vec<Box<dyn SchematicConnector>>,
    |         --------------------------------------------------

error[E0599]: no method named `downcast_mut` found for mutable reference `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` in the current scope
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:102:34
    |
102 |                 match connection.downcast_mut::<RightAngle>() {
    |                                  ^^^^^^^^^^^^
    |
help: there is a method `as_mut` with a similar name
    |
102 -                 match connection.downcast_mut::<RightAngle>() {
102 +                 match connection.as_mut::<RightAngle>() {
    |

error[E0282]: type annotations needed
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:107:25
    |
107 |                         ra.move_end1_position(drag_delta);
    |                         ^^ cannot infer type

error[E0599]: no method named `move_end1_position` found for mutable reference `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` in the current scope
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:133:28
    |
133 |                 connection.move_end1_position(drag_delta);
    |                            ^^^^^^^^^^^^^^^^^^ method not found in `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>`

error[E0599]: no method named `containing_rect` found for mutable reference `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` in the current scope
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:139:54
    |
139 |             let inner_response = ui.place(connection.containing_rect(), &mut *connection);
    |                                                      ^^^^^^^^^^^^^^^ method not found in `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>`

error[E0277]: expected a `FnMut(&mut egui::Ui)` closure, found `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
    --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:139:73
     |
 139 |             let inner_response = ui.place(connection.containing_rect(), &mut *connection);
     |                                     -----                               ^^^^^^^^^^^^^^^^ expected an `FnMut(&mut egui::Ui)` closure, found `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
     |                                     |
     |                                     required by a bound introduced by this call
     |
     = help: the trait `for<'a> FnMut(&'a mut egui::Ui)` is not implemented for `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
     = help: the following other types implement trait `egui::Widget`:
               &egui::epaint::PaintStats
               &mut datatypes::schematic_connector::ConnectionPoint
               &mut datatypes::schematic_connector::multi_right_angle::MultiRightAngle
               &mut datatypes::schematic_connector::right_angle::RightAngle
               &mut datatypes::schematic_symbol::SchematicSymbol
               &mut egui::CornerRadius
               &mut egui::FontTweak
               &mut egui::Frame
             and 19 others
     = note: required for `std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `for<'a> FnMut(&'a mut egui::Ui)`
     = note: required for `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `for<'a> FnOnce(&'a mut egui::Ui)`
     = note: required for `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `egui::Widget`
note: required by a bound in `egui::Ui::place`
    --> /home/toxicsauce/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui-0.33.3/src/ui.rs:1813:58
     |
1813 |     pub fn place(&mut self, max_rect: Rect, widget: impl Widget) -> Response {
     |                                                          ^^^^^^ required by this bound in `Ui::place`

error[E0277]: expected a `FnMut(&mut egui::Ui)` closure, found `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
    --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:139:78
     |
 139 |             let inner_response = ui.place(connection.containing_rect(), &mut *connection);
     |                                     -----                                    ^^^^^^^^^^^ expected an `FnMut(&mut egui::Ui)` closure, found `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
     |                                     |
     |                                     required by a bound introduced by this call
     |
     = help: the trait `for<'a> FnMut(&'a mut egui::Ui)` is not implemented for `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
     = help: the following other types implement trait `egui::Widget`:
               &egui::epaint::PaintStats
               &mut datatypes::schematic_connector::ConnectionPoint
               &mut datatypes::schematic_connector::multi_right_angle::MultiRightAngle
               &mut datatypes::schematic_connector::right_angle::RightAngle
               &mut datatypes::schematic_symbol::SchematicSymbol
               &mut egui::CornerRadius
               &mut egui::FontTweak
               &mut egui::Frame
             and 19 others
     = note: required for `std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `for<'a> FnMut(&'a mut egui::Ui)`
     = note: required for `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `for<'a> FnOnce(&'a mut egui::Ui)`
     = note: required for `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `egui::Widget`
note: required by a bound in `egui::Ui::place`
    --> /home/toxicsauce/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui-0.33.3/src/ui.rs:1813:58
     |
1813 |     pub fn place(&mut self, max_rect: Rect, widget: impl Widget) -> Response {
     |                                                          ^^^^^^ required by this bound in `Ui::place`

error[E0599]: no method named `move_midpoint` found for mutable reference `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` in the current scope
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:153:28
    |
153 |                 connection.move_midpoint(inner_response.drag_delta());
    |                            ^^^^^^^^^^^^^ method not found in `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>`

error[E0599]: no method named `containing_rect` found for mutable reference `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` in the current scope
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:158:54
    |
158 |             let inner_response = ui.place(connection.containing_rect(), &mut *connection);
    |                                                      ^^^^^^^^^^^^^^^ method not found in `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>`

error[E0277]: expected a `FnMut(&mut egui::Ui)` closure, found `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
    --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:158:73
     |
 158 |             let inner_response = ui.place(connection.containing_rect(), &mut *connection);
     |                                     -----                               ^^^^^^^^^^^^^^^^ expected an `FnMut(&mut egui::Ui)` closure, found `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
     |                                     |
     |                                     required by a bound introduced by this call
     |
     = help: the trait `for<'a> FnMut(&'a mut egui::Ui)` is not implemented for `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
     = help: the following other types implement trait `egui::Widget`:
               &egui::epaint::PaintStats
               &mut datatypes::schematic_connector::ConnectionPoint
               &mut datatypes::schematic_connector::multi_right_angle::MultiRightAngle
               &mut datatypes::schematic_connector::right_angle::RightAngle
               &mut datatypes::schematic_symbol::SchematicSymbol
               &mut egui::CornerRadius
               &mut egui::FontTweak
               &mut egui::Frame
             and 19 others
     = note: required for `std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `for<'a> FnMut(&'a mut egui::Ui)`
     = note: required for `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `for<'a> FnOnce(&'a mut egui::Ui)`
     = note: required for `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `egui::Widget`
note: required by a bound in `egui::Ui::place`
    --> /home/toxicsauce/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui-0.33.3/src/ui.rs:1813:58
     |
1813 |     pub fn place(&mut self, max_rect: Rect, widget: impl Widget) -> Response {
     |                                                          ^^^^^^ required by this bound in `Ui::place`

error[E0277]: expected a `FnMut(&mut egui::Ui)` closure, found `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
    --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:158:78
     |
 158 |             let inner_response = ui.place(connection.containing_rect(), &mut *connection);
     |                                     -----                                    ^^^^^^^^^^^ expected an `FnMut(&mut egui::Ui)` closure, found `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
     |                                     |
     |                                     required by a bound introduced by this call
     |
     = help: the trait `for<'a> FnMut(&'a mut egui::Ui)` is not implemented for `(dyn datatypes::schematic_connector::SchematicConnector + 'static)`
     = help: the following other types implement trait `egui::Widget`:
               &egui::epaint::PaintStats
               &mut datatypes::schematic_connector::ConnectionPoint
               &mut datatypes::schematic_connector::multi_right_angle::MultiRightAngle
               &mut datatypes::schematic_connector::right_angle::RightAngle
               &mut datatypes::schematic_symbol::SchematicSymbol
               &mut egui::CornerRadius
               &mut egui::FontTweak
               &mut egui::Frame
             and 19 others
     = note: required for `std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `for<'a> FnMut(&'a mut egui::Ui)`
     = note: required for `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `for<'a> FnOnce(&'a mut egui::Ui)`
     = note: required for `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` to implement `egui::Widget`
note: required by a bound in `egui::Ui::place`
    --> /home/toxicsauce/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/egui-0.33.3/src/ui.rs:1813:58
     |
1813 |     pub fn place(&mut self, max_rect: Rect, widget: impl Widget) -> Response {
     |                                                          ^^^^^^ required by this bound in `Ui::place`

error[E0599]: no method named `move_midpoint` found for mutable reference `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` in the current scope
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:172:28
    |
172 |                 connection.move_midpoint(inner_response.drag_delta());
    |                            ^^^^^^^^^^^^^ method not found in `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>`

error[E0631]: type mismatch in function arguments
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:279:18
    |
279 |             .map(RightAngle::containing_rect)
    |              --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected due to this
    |              |
    |              required by a bound introduced by this call
    |
   ::: crates/cdm_core/src/datatypes/schematic_connector/right_angle.rs:181:5
    |
181 |     pub fn containing_rect(&self) -> Rect {
    |     ------------------------------------- found signature defined here
    |
    = note: expected function signature `fn(&std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>) -> _`
               found function signature `fn(&datatypes::schematic_connector::right_angle::RightAngle) -> _`
note: required by a bound in `std::iter::Iterator::map`
   --> /rustc/e408947bfd200af42db322daf0fadfe7e26d3bd1/library/core/src/iter/traits/iterator.rs:777:4
help: consider wrapping the function in a closure
    |
279 |             .map(|arg0: &std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>| RightAngle::containing_rect(/* &datatypes::schematic_connector::right_angle::RightAngle */))
    |                  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++                            ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

error[E0599]: the method `collect` exists for struct `Map<Chain<Iter<'_, Box<dyn SchematicConnector>>, Iter<'_, Box<dyn SchematicConnector>>>, ...>`, but its trait bounds were not satisfied
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:280:14
    |
278 |           let connector_rects: Vec<Rect> = chain(&self.end1_connections, &self.end2_connections)
    |  __________________________________________-
279 | |             .map(RightAngle::containing_rect)
280 | |             .collect();
    | |             -^^^^^^^ method cannot be called due to unsatisfied trait bounds
    | |_____________|
    |
    |
    = note: the following trait bounds were not satisfied:
            `<for<'a> fn(&'a datatypes::schematic_connector::right_angle::RightAngle) -> egui::Rect {datatypes::schematic_connector::right_angle::RightAngle::containing_rect} as std::ops::FnOnce<(&std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>,)>>::Output = _`
            which is required by `std::iter::Map<std::iter::Chain<std::slice::Iter<'_, std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>>, std::slice::Iter<'_, std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>>>, for<'a> fn(&'a datatypes::schematic_connector::right_angle::RightAngle) -> egui::Rect {datatypes::schematic_connector::right_angle::RightAngle::containing_rect}>: std::iter::Iterator`
            `for<'a> fn(&'a datatypes::schematic_connector::right_angle::RightAngle) -> egui::Rect {datatypes::schematic_connector::right_angle::RightAngle::containing_rect}: std::ops::FnMut<(&std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>,)>`
            which is required by `std::iter::Map<std::iter::Chain<std::slice::Iter<'_, std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>>, std::slice::Iter<'_, std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>>>, for<'a> fn(&'a datatypes::schematic_connector::right_angle::RightAngle) -> egui::Rect {datatypes::schematic_connector::right_angle::RightAngle::containing_rect}>: std::iter::Iterator`
            `std::iter::Map<std::iter::Chain<std::slice::Iter<'_, std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>>, std::slice::Iter<'_, std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>>>, for<'a> fn(&'a datatypes::schematic_connector::right_angle::RightAngle) -> egui::Rect {datatypes::schematic_connector::right_angle::RightAngle::containing_rect}>: std::iter::Iterator`
            which is required by `&mut std::iter::Map<std::iter::Chain<std::slice::Iter<'_, std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>>, std::slice::Iter<'_, std::boxed::Box<dyn datatypes::schematic_connector::SchematicConnector>>>, for<'a> fn(&'a datatypes::schematic_connector::right_angle::RightAngle) -> egui::Rect {datatypes::schematic_connector::right_angle::RightAngle::containing_rect}>: std::iter::Iterator`
    = note: the full name for the type has been written to '/home/toxicsauce/projects/src/github.com/sww1235/connection-diagram-manager/target/debug/deps/cdm_core-efe29431665cd958.long-type-7632612688941049871.txt'
    = note: consider using `--verbose` to print the full type name to the console

error[E0282]: type annotations needed
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:283:24
    |
283 |             .flat_map(|rect| [rect.left_top(), rect.right_bottom()].to_vec())
    |                        ^^^^   ---- type must be known at this point
    |
help: consider giving this closure parameter an explicit type
    |
283 |             .flat_map(|rect: /* Type */| [rect.left_top(), rect.right_bottom()].to_vec())
    |                            ++++++++++++

warning: unreachable pattern
  --> crates/cdm_core/src/datatypes/color.rs:97:26
   |
97 |             Self::GRAY | Self::GREY => "GRY".to_owned(),
   |             ----------   ^^^^^^^^^^ no value can reach this
   |             |
   |             matches all the relevant values
   |
   = note: `#[warn(unreachable_patterns)]` (part of `#[warn(unused)]`) on by default

warning: unreachable pattern
  --> crates/cdm_core/src/datatypes/color.rs:92:13
   |
91 |             Self::PINK => "PNK".to_owned(),
   |             ---------- matches all the relevant values
92 |             Self::ROSE => "RSE".to_owned(),
   |             ^^^^^^^^^^ no value can reach this

warning: unreachable pattern
  --> crates/cdm_core/src/datatypes/color.rs:98:13
   |
97 |             Self::GRAY | Self::GREY => "GRY".to_owned(),
   |             ----------------------- matches all the relevant values
98 |             Self::SLATE => "SLT".to_owned(),
   |             ^^^^^^^^^^^ no value can reach this

warning: unreachable pattern
  --> crates/cdm_core/src/datatypes/color.rs:99:13
   |
96 |             Self::WHITE => "WHT".to_owned(),
   |             ----------- matches all the relevant values
...
99 |             Self::CLEAR => "CLR".to_owned(),
   |             ^^^^^^^^^^^ no value can reach this

warning: unreachable pattern
   --> crates/cdm_core/src/datatypes/color.rs:101:13
    |
100 |             Self::CYAN => "CYN".to_owned(),
    |             ---------- matches all the relevant values
101 |             Self::AQUA => "AQA".to_owned(),
    |             ^^^^^^^^^^ no value can reach this

warning: unused variable: `equipment_id`
   --> crates/cdm_core/src/datatypes/project_types/equipment.rs:336:53
    |
336 | ...                   equipment_id,
    |                       ^^^^^^^^^^^^ help: try ignoring the field: `equipment_id: _`
    |
    = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default

warning: unused variable: `equipment_id`
   --> crates/cdm_core/src/datatypes/project_types/equipment.rs:346:53
    |
346 | ...                   equipment_id,
    |                       ^^^^^^^^^^^^ help: try ignoring the field: `equipment_id: _`

warning: unused variable: `equipment_id`
   --> crates/cdm_core/src/datatypes/project_types/equipment.rs:367:53
    |
367 | ...                   equipment_id,
    |                       ^^^^^^^^^^^^ help: try ignoring the field: `equipment_id: _`

warning: unused variable: `term_strip_id`
   --> crates/cdm_core/src/datatypes/project_types/equipment.rs:372:57
    |
372 | ...                   term_strip_id,
    |                       ^^^^^^^^^^^^^ help: try ignoring the field: `term_strip_id: _`

warning: unused variable: `equipment_id`
   --> crates/cdm_core/src/datatypes/project_types/equipment.rs:380:53
    |
380 | ...                   equipment_id,
    |                       ^^^^^^^^^^^^ help: try ignoring the field: `equipment_id: _`

warning: unused variable: `term_strip_id`
   --> crates/cdm_core/src/datatypes/project_types/equipment.rs:385:57
    |
385 | ...                   term_strip_id,
    |                       ^^^^^^^^^^^^^ help: try ignoring the field: `term_strip_id: _`

Some errors have detailed explanations: E0277, E0282, E0308, E0599, E0631.
For more information about an error, try `rustc --explain E0277`.
warning: `connection_diagram_manager` (lib) generated 12 warnings
error: could not compile `connection_diagram_manager` (lib) due to 19 previous errors; 12 warnings emitted

Given that this output is so long, and was vanishing out of my terminal, I was trying to keep things brief.

A lot of the errors are related to other traits breaking due to the Box<> not derefing.

MultiRightAngle and &mut MultiRightAngle are different types.
you may choose to implement the SchematicConnector trait for either one, or both, depending on your use case. which ever type you implement the trait for, you must also implement the super trait, i.e. Widget, otherwise, you get the first compilation error.

If I change the impl to &mut SchematicConnector, I get the below error, with lots of issues around lifetimes.

error[E0478]: lifetime bound not satisfied
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:44:6
   |
44 | impl SchematicConnector for &mut MultiRightAngle {}
   |      ^^^^^^^^^^^^^^^^^^
   |
note: lifetime parameter instantiated with the anonymous lifetime as defined here
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:44:29
   |
44 | impl SchematicConnector for &mut MultiRightAngle {}
   |                             ^
   = note: but lifetime parameter must outlive the static lifetime

error[E0803]: cannot infer an appropriate lifetime for lifetime parameter `'_` due to conflicting requirements
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:44:29
   |
44 | impl SchematicConnector for &mut MultiRightAngle {}
   |                             ^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime as defined here...
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:44:29
   |
44 | impl SchematicConnector for &mut MultiRightAngle {}
   |                             ^
note: ...so that the types are compatible
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:44:29
   |
44 | impl SchematicConnector for &mut MultiRightAngle {}
   |                             ^^^^^^^^^^^^^^^^^^^^
   = note: expected `<&mut datatypes::schematic_connector::multi_right_angle::MultiRightAngle as datatypes::schematic_connector::SchematicConnector>`
              found `<&mut datatypes::schematic_connector::multi_right_angle::MultiRightAngle as datatypes::schematic_connector::SchematicConnector>`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the declared lifetime parameter bounds are satisfied
  --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:44:29
   |
44 | impl SchematicConnector for &mut MultiRightAngle {}
   |                             ^^^^^^^^^^^^^^^^^^^^

Thanks for this!

The fact that I have to add a helper method is very annoying, but manageable, and makes the method signatures much cleaner!

For completeness: assuming you're talking about the helper method for casting to dyn Any - you could also define an inherent method that's just called downcast_mut and does the same kind of thing as the one on dyn Any, and the call site would look even cleaner again. I just tried to outline the simplest approach of "just help out type inference do the right cast" as one workable solution, not necessarily the cleanest API possible :wink:

By the way, full output of cargo clippy is totally fine as well (as you can see, the block just becomes scrollable, so your forum post remains well readable) — but more precisely, what people meant by "full error message" would have been: a full single section from the output. For example this is one "full" error message

error[E0599]: no method named `downcast_mut` found for mutable reference `&mut std::boxed::Box<(dyn datatypes::schematic_connector::SchematicConnector + 'static)>` in the current scope
   --> crates/cdm_core/src/datatypes/schematic_connector/multi_right_angle.rs:102:34
    |
102 |                 match connection.downcast_mut::<RightAngle>() {
    |                                  ^^^^^^^^^^^^
    |
help: there is a method `as_mut` with a similar name
    |
102 -                 match connection.downcast_mut::<RightAngle>() {
102 +                 match connection.as_mut::<RightAngle>() {
    |

and that way of presenting the single error - including all the code and excerpts and notes that come with it (all the way up to [but not including] the next error or warning after it) is much preferred over just quoting the “no method named downcast_mut …” part from the first line :ferris_happy:

While the pointer to the code location was not the most important for this particular error, and the help suggestion wasn't all that useful either – with other cases, e. g. borrow checker errors, the bottom part of the error message can be a lot more relevant for more easily understanding what's going on / what's the problem.

My other problem with providing full error messages, is I don't have functioning terminal scrollback (something on the todo list). Hence why it was easier to output the full result of cargo clippy to a file.

Thanks for the help regardless.

What IDE are you using? Some do offer a way to show the full error message, too :slight_smile:


For example in vscode, there’s a button for this (in several places):

which takes you into a new tab with just that single error message, but as a complete diagnostic as if it was in the terminal and easy to copy-paste


For displaying Rust errors in a terminal, I love the tool bacon[1] which by the way, as a TUI application, also offers its own scrolling functionality, anyway :slight_smile:


  1. I do often end up opening that even next to my graphical IDE, because I sometimes prefer the quick overview over all errors it can offer ↩︎

What IDE are you using?

Vim.

Its an issue with st on void, that I need to add a patch to fix, just one thing that hasn't gotten annoying enough to put to the top of the list of priorities.

bacon looks very interesting, I will give it a try!

I reverted back to the enum method, as all the traits were making my head spin.

Impl Widget for &mut SchematicConnector did resolve a number of the issues with Widget specifically. Thanks for the suggestion.

Going to mark this as the solved answer, since it is the direction I am going to proceed for now.

Maybe at some point, i will try and experiment with some of the trait stuff again.

I appreciate all the help and explanations around the trait stuff.