Calling a method on a trait object without importing its trait definition

Hi,

I have a question about a behavior of trait objects.

Is it allowed in Rust to call a method on a trait object without importing its trait definition?

I tried to build the following code and got E0559 as described in "3.19. Traits" in TRPL.

mod dom {
    pub trait Element {
        fn tag_name(&self) -> &str;
    }
}

struct Element {}

impl dom::Element for Element {
    fn tag_name(&self) -> &str {
        "span"
    }
}

fn main() {
    let element = Box::new(Element {});
    println!("{}", element.tag_name());
}

The build error can be fixed by adding use dom::Element as DomElement;.

But the following code can be built and run successfully without importing dom::Element in the scope.

mod dom {
    pub trait Element {
        fn tag_name(&self) -> &str;
    }
}

struct Element {}

impl dom::Element for Element {
    fn tag_name(&self) -> &str {
        "span"
    }
}

fn main() {
    let element: Box<dom::Element> = Box::new(Element {});
    println!("{}", element.tag_name());
}

I'm not sure, but using the qualified trait name seems to cause this behavior.

Thank you for your kindly support.

The issue here is that in the first example you essentially create a Box<Element> and you let the type of the element local be inferred, while in the second example you create a Box<Element> and then explicitly coerce it to a dynamic trait object by specifying Box<dom::Element> as a type for the element local.

Correspondingly, in the first example the compiler looks for the tag_name() method in the list of inherent methods for the Element struct (of which there are none), while in the second example, the only things that methods can even be called on are the Box<_> component of the type and the dom::Element argument to Box.
Given that Box has no method named tag_name(), the only place left to look is in the list of trait methods.

Therefore the build issue being fixed in the first example by adding use dom::Element; is because a trait needs to be in scope before it can be used. So in this case, by adding the use statement, the compiler knows that in addition to looking at the list of inherent methods, it must also have a look at the list of trait methods.

As a side note: It's generally a bad idea to give traits the same name as an existing struct or enum. Even in paragraph above keeping the 2 straight costs more effort than it would otherwise, despite the explicit namespacing for the trait.

Thank you for your reply and advice.

Probably, I have understood what you pointed out.
But, I like to know whether this behavior is intended or not in Rust.

If this behavior is intended, methods which are defined in a non-imported trait can be called via a trait object having the vtable for the non-imported trait.
That seems to break the first rule described in "3.19. Traits".

I think that this behavior is reasonable and is not like the Wild West described in "3.19. Traits".
Because this behavior occurs only when we (implicitly or explicitly) create such a trait object.

I suggest you use dyn; it helps greatly in making it more clear when something is using a trait.

1 Like

Thank you for your advice.
I'll keep that in mind.