Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In 17.2 (Using Trait Objects...) the text for the example comparing trait objects with generics is incorrect. #3778

Open
jumpnbrownweasel opened this issue Nov 15, 2023 · 1 comment

Comments

@jumpnbrownweasel
Copy link

jumpnbrownweasel commented Nov 15, 2023

URL to the section(s) of the book with this problem:

https://doc.rust-lang.org/book/ch17-02-trait-objects.html#defining-a-trait-for-common-behavior
https://doc.rust-lang.org/book/ch17-02-trait-objects.html#implementing-the-trait

Description of the problem:

The last paragraph of Defining a Trait for Common Behavior:

On the other hand, with the method using trait objects, one Screen instance can hold a Vec<T> that contains a Box<Button> as well as a Box<TextField>. Let’s look at how this works, and then we’ll talk about the runtime performance implications.

is incorrect, or at the least extremely misleading: Vec<T> should be Vec<Box<dyn Draw>>. Vec<T> is the type in the example code that uses generics, not trait objects, while Vec<Box<dyn Draw>> must be used in the example code using trait objects. The paragraph above is referring to the approach for trait objects, not generics.

This mistake leads the reader to think that Vec<T> should be used with the example code for trait objects. This adds to the confusion caused by another issue with the text in this section, which I believe should not have been closed.

Suggested fix:
A minimal fix is to change Vec<T> to Vec<Box<dyn Draw>> in the paragraph mentioned above.

In addition I think it would avoid a lot of confusion (see this URLO post) to repeat the following type near the top of the Implementing the Trait section:

pub struct Screen {
    pub components: Vec<Box<dyn Draw>>,
}

This would make it clear that the Implementing the Trait section is referring to this type, not the last shown definition of that type (which uses Vec<T>). The example code in this section will not compile if Vec<T> is used.

I would be happy to take a shot at fixing this if it is approved.

@chriskrycho chriskrycho added this to the ch17 milestone Apr 1, 2024
@chriskrycho
Copy link
Contributor

Hmm, I see the confusion here, but I think there is actually a deeper level of confusion highlighted by your comment. (To be extra clear, I am not saying this is your fault or problem!) When you write this—

The paragraph above is referring to the approach for trait objects, not generics.

—it indicates that you saw trait objects as being an alternative to generics. The text more or less says that, so: fair! But they are not—at least, not exactly. They are a different feature, which can be used with generics, and I think the confusion here (as well as in the other issue you opened) is due to the two related, but ultimately distinct, ways that the text uses “generics” here: to refer to Vec<T> as a component of Screen, and how to define Screen itself—with a generic using a trait bound, or just requiring a trait object. That is, the difference the text cares about is:

  • Screen using trait object:

    pub struct Screen {
        pub components: Vec<Box<dyn Draw>>,
    }
  • Screen using constrained generic:

    pub struct Screen<T: Draw> {
        pub components: Vec<T>,
    }

The thing the text is distinguishing in this section is thus not between generics and trait objects per se, but rather two different ways you can specify what the generic type THIS TYPE is in the definition components: Vec<THIS TYPE>. The distinction is between using a trait objects there vs. a generic from Screen<T> with a trait bound. So I can see why at least some people are getting stuck here!

Because Vec<T> and Box<T> remain the normal ways to refer to those generic types, we might want to find a way to be a tiny bit clearer about when we are talking about them vs. when we are talking about Screen vs. Screen<T>. 🤔

Note

When you write Vec<Box<dyn Draw>>, you are still writing a specific instantiation of a generic type Vec<T>, as well as of the generic type Box<T>. Vec is always a “generic type”, generic over whatever T a given usage instantiates it with. (So is Box.) One way you can get a concrete type for it is with a “static” type like String, for example with Vec<String>. Another is with a pointer wrapping dyn Trait, for example with Vec<Box<dyn Draw>>. Both of those are a specific kind of Vec<T>. The text is trying to walk a fine line between precision/pedantry and saying just enough to cover the basic idea and keep moving toward the goal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants