Skip to content

Make the documentation of From and Into traits coherent. #59163

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

Closed
DevQps opened this issue Mar 13, 2019 · 10 comments · Fixed by #59330
Closed

Make the documentation of From and Into traits coherent. #59163

DevQps opened this issue Mar 13, 2019 · 10 comments · Fixed by #59330
Labels
A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools

Comments

@DevQps
Copy link
Contributor

DevQps commented Mar 13, 2019

Description

I was reading on the From and Into traits and I got a little confused:

From states:

Simple and safe type conversions in to Self.

whereas Into states:

A conversion that consumes self, which may or may not be expensive.

The Problem

This makes it seem like implementing From should be used for implementing relatively cheap operations (interpreting the word 'simple' as 'cheap') automatically gaining Into for free. Whereas implementing Into (and therefore not getting the From trait for free) should be used for expensive conversions.

However later in the documentation of Into states:

Library authors should not directly implement this trait, but should prefer implementing the From trait, which offers greater flexibility and provides an equivalent Into implementation for free, thanks to a blanket implementation in the standard library.

which in turns suggests that one should not implement Into at all (unless you want to convert into a type outside your crate).

Now I have 3 questions:

  1. Is it true, that From should only be used for inexpensive conversions? (If not, we should probably change the documentation of both From and Into to refrain from saying things about expensiveness).

  2. The documentation of Into states that one should only implement Into if you want to convert into a type outside your crate. From cannot do this as the example shows. However the significant limitation is that you do not get the From trait for free, meaning that library authors using From as trait bounds cannot automatically use the Into implementation. Is there a reason why implementing Into does not automatically implement From as well?

  3. Into nicely states that it should only be used when implementing traits for types in external crates. Maybe the documentation of From should also note this?

I am curious on what you all think!

Disclaimer: I have been using Rust for the last 4 months, so I would not consider myself an expert. So if I am wrong on some points or misunderstand anything, please correct me!

@steveklabnik steveklabnik added the A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools label Mar 13, 2019
@steveklabnik
Copy link
Member

I'll respond more thoroughly to this later, but

Disclaimer: I have been using Rust for the last 4 months, so I would not consider myself an expert. So if I am wrong on some points or misunderstand anything, please correct me!

This is a good thing, not a bad thing! The docs should be legible to those who are not experts. In many ways, I prefer feedback from someone like you than to someone who's been writing Rust for years!

@lwshang
Copy link
Contributor

lwshang commented Mar 13, 2019

There is a good blog provide relative knowledge. https://ricardomartins.cc/2016/08/03/convenient_and_idiomatic_conversions_in_rust

Might be a good reference here.

@DevQps
Copy link
Contributor Author

DevQps commented Mar 13, 2019

@lwshang Thanks for the link! I hopefully will find some time to read it tomorrow. If the article gives me some insights that require changes in this issue I'll edit the description (Y).

@DevQps
Copy link
Contributor Author

DevQps commented Mar 14, 2019

@lwshang I was reading through the post and some things about costs caught my eye:

From, Into, and their fallible counterparts, TryFrom and TryInto, operate on values and perform conversions that may be costly;

So this basically says that From, Into, TryFrom and TryInto say nothing about how costly the actual conversion is.

AsRef and AsMut, on the other hand, take references to values and perform cheap reference-to-reference conversions;

Does this mean that programmers that wish to convert references to references using an expensive conversion way should not use AsRef and AsMut?

We can make our methods more general by using those traits as type constraints.

I have seen this quite often as a good practice! (Depending on the scenario I guess?) Maybe we should include examples of this as well?

If you all agree on these points I don't mind doing an attempt to improve it!

@lwshang
Copy link
Contributor

lwshang commented Mar 14, 2019

To you last comment

So this basically says that From, Into, TryFrom and TryInto say nothing about how costly the actual conversion is.

Agree, there is no implication for the cost of the conversion from these traits.

Does this mean that programmers that wish to convert references to references using an expensive conversion way should not use AsRef and AsMut?

I believe that the reference to reference conversion usually do less copy so that it is cheaper. So if we have to do heavy calculation before we can return a reference of the new type, we should better not to use AsRef or AsMut.

I went through the implementors of trait AsRef. I infer that the two types involved in the conversion of AsMut and AsRef should better to have some level of similarity in memory layout. This is just my inference, it's better to get comments from others.

Return to your original questions:

For the first question, I agree that the Simple is not a clear term. My understanding is that the conversion made by From trait should be intuitive. std implements impl From<[u16; 8]> for Ipv6Addr, this is obviously intuitive. std also implements impl<T> From<BinaryHeap<T>> for Vec<T>, even though these two data structures have very different underlying implementation, they do share the abstract that they are data structures that is collections of some type. So this conversion is also intuitive.

Therefore, there is actually no inference from simple to cheap computation. Of course, we can improve the document by clarifying this distinction.

The second and third question is relative. The context there is that the crate writer should implement traits for the struct (T) of that crate. If writer want to convert T to some type (U) outside the crate, it should be impl into<U> for T. If writer want to covert some type (U) outside the crate to T, it should be impl From<U> for T. Stick to impl SomeTrait for T. I'm not sure whether this rule is that strict or just a kind of best practice.

@DevQps
Copy link
Contributor Author

DevQps commented Mar 18, 2019

I'll respond more thoroughly to this later, but

Disclaimer: I have been using Rust for the last 4 months, so I would not consider myself an expert. So if I am wrong on some points or misunderstand anything, please correct me!

This is a good thing, not a bad thing! The docs should be legible to those who are not experts. In many ways, I prefer feedback from someone like you than to someone who's been writing Rust for years!

@steveklabnik I don't want to bother you if you didn't forget it but just have been busy with other stuff, but just in case you forgot here another reminder! I am curious to what your view is on this.

@steveklabnik
Copy link
Member

steveklabnik commented Mar 18, 2019

I had it on my to-do list for this morning, you beat me to it :)

Is it true, that From should only be used for inexpensive conversions? (If not, we should probably change the documentation of both From and Into to refrain from saying things about expensiveness).

Yes, there's no expensiveness requirement here, so we shouldn't talk about it.

The documentation of Into states that one should only implement Into if you want to convert into a type outside your crate. From cannot do this as the example shows. However the significant limitation is that you do not get the From trait for free, meaning that library authors using From as trait bounds cannot automatically use the Into implementation. Is there a reason why implementing Into does not automatically implement From as well?

It's due to coherence; this would mean that the implementations would be recursive. Anything that implements from would implement into which implements from which implements into which....

Into nicely states that it should only be used when implementing traits for types in external crates. Maybe the documentation of From should also note this?

Yeah, I think pointing people to Into makes a lot of sense.

If you all agree on these points I don't mind doing an attempt to improve it!

Please do!

@DevQps
Copy link
Contributor Author

DevQps commented Mar 18, 2019

@steveklabnik Thanks for your response! I will put it on my list and hope to create a PR for it somewhere this week! I'll tag you again once it's done!

@DevQps
Copy link
Contributor Author

DevQps commented Mar 18, 2019

@steveklabnik Sorry for bothering you again, but I have a few more questions!:

Module Documentation

In the module description of std::convert we can read the following:

The From trait is the most flexible, useful for value and reference conversions

Question: In what scenarios should we use implement a reference-to-reference conversion using From instead of AsRef? Or is what they mean here: reference-to-value conversion? (i.e. &str to String).

We can also read there:

...., as From and TryFrom provide greater flexibility and offer equivalent Into or TryInto implementations for free.

Question: What is this "greater flexibility" compared to the Into trait? Apparently it's more then the "free Into implementation" since the text uses 'and offer ...'.

AsRef / AsMut Documentation

I see that AsRef and AsMut also make statements about cost, multiple times.

AsRef: A cheap reference-to-reference conversion.

Question: Does this mean that programmers that wish to convert references to references using an expensive conversion way should not use AsRef and AsMut but rather a dedicated method? Otherwise I will probably be nice to add that explicitly to the documentation as well!

Just asking these questions such that I improve the docs better!

@steveklabnik
Copy link
Member

Question: In what scenarios should we use implement a reference-to-reference conversion using From instead of AsRef? Or is what they mean here: reference-to-value conversion? (i.e. &str to String).

I think this is also bad wording; I would just remove this whole sentence.

Question: What is this "greater flexibility" compared to the Into trait? Apparently it's more then the "free Into implementation" since the text uses 'and offer ...'.

Since these can fail, you can use them in more situations. That is, you can imagine that every body of From calls TryFrom, and panics on error. Does that make sense?

That said, you shouldn't actually do that, as they're for infallible conversions, but since TryFrom can work for fallible and infallible, it's more flexible.

Question: Does this mean that programmers that wish to convert references to references using an expensive conversion way should not use AsRef and AsMut

Yes.

but rather a dedicated method? Otherwise I will probably be nice to add that explicitly to the documentation as well!

By implementing From on &T, I think.

Does that make sense?

Just asking these questions such that I improve the docs better!

Absolutely, these are great questions. I'm looking forward to it!

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Mar 26, 2019
…ation, r=steveklabnik

Improve the documentation for std::convert (From, Into, AsRef and AsMut)

# Description
In this PR I updated the documentation of From, Into, AsRef and AsMut, as well as the general std::convert module documentation. The discussion in rust-lang#59163 provided information that was not yet present in the docs, or was not expressed clearly enough. I tried to clarify the examples that were already present in the docs as well as add more information about considered best-practices that came out of the discussion in rust-lang#59163

@steveklabnik I hope I didn't change too much. This is an initial version! I will scan through everything tomorrow as well again to see if I made any typo's or errors, and maybe make some small changes here and there.

All suggestions are welcome!

closes rust-lang#59163
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants