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

Borrow and AsRef are redundant #24140

Closed
nwin opened this issue Apr 7, 2015 · 13 comments
Closed

Borrow and AsRef are redundant #24140

nwin opened this issue Apr 7, 2015 · 13 comments
Assignees
Labels
A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools C-enhancement Category: An issue proposing an enhancement or a PR with one. P-high High priority

Comments

@nwin
Copy link
Contributor

nwin commented Apr 7, 2015

These traits have exactly the same signature, both are stable and both should be implemented by the same types. One of them should go.

Furthermore AsRef is not implemented by Box etc…

@aturon
Copy link
Member

aturon commented Apr 7, 2015

There are a few really important differences:

  • Borrow is used by data structures like HashMap and BTreeMap which assume that the result of hashing or ordering based on owned and borrowed values is the same. But there are many AsRef conversions for which this isn't true. So in general, Borrow should be implemented for fewer types than AsRef, because it makes stronger guarantees.
  • Borrow provides a blanket implementation T: Borrow<T>, which is essential for making the above collections work well. AsRef provides a different blanket implementation, basically &T: AsRef<U> whenever T: AsRef<U>, which is important for APIs like fs::open that can use a simpler and more flexible signature as a result. You can't have both blanket implementations due to coherence, so each trait is making the choice that's appropriate for its use case.

This should be better documented, but there's definitely a reason that things are set up this way.

As to AsRef on Box and friends, the initial implementations were somewhat minimal, and we've been adding more over time.

I'm going to close this issue for now, since the traits are not in fact redundant. But it may be worth opening a separate issue for improved documentation and missing impls on AsRef.

@frewsxcv
Copy link
Member

For the sake of cross-posting, I just asked this same question in Discourse without knowing this Issue existed.

@quadrupleslap
Copy link

quadrupleslap commented Jul 31, 2017

Still not properly documented (I think.)

Edit: The only explanation I can find is on the AsRef page and it's much more vague than @aturon's amazing explanation here.

@frewsxcv frewsxcv reopened this Jul 31, 2017
@frewsxcv frewsxcv added the A-docs Area: Documentation for any part of the project, including the compiler, standard library, and tools label Jul 31, 2017
@steveklabnik
Copy link
Member

https://doc.rust-lang.org/book/first-edition/borrow-and-asref.html exists, but it's not going to in the second edition

@Mark-Simulacrum Mark-Simulacrum added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Aug 3, 2017
@steveklabnik steveklabnik added the P-high High priority label Aug 30, 2017
@steveklabnik steveklabnik self-assigned this Aug 30, 2017
@prasannavl
Copy link

prasannavl commented Nov 19, 2017

@aturon - Thanks for a better explanation - However from what I understand the only place where Borrow really differs is while borrowing a reference for hashing. So would it not make sense to have the trait be AsHashableRef (or something similar) and method be as_hashable_ref or something similar? Hopefully some one else can come up with a shorter name that makes sense.

It is rather verbose - but it's intentions are much more clear. Borrow is just confusing in the context.

PS: I could be wrong, but my brain is likely misleading me into thinking I have good understanding after reading the above statement, but I'm not entirely sure if I'm clear on the kind of consequences on implementing one over the other would cause. Would be better if there are side-by-side comparisons of both, in the documentation to express it clearly.

@prasannavl
Copy link

prasannavl commented Nov 19, 2017

Ah. Alright, I realize I was wrong - and my above statement is incorrect.

Finally, this was one only post that provided the most sensible documentation so far:
#44868

Thanks to @stjepang for this.

PS: All other documentation including the above comment just focuses so much only hasing - it still doesn't provide a complete picture and possibly misleading - even though it's right.

@partim
Copy link
Contributor

partim commented Nov 27, 2017

I’m not sure if this is the best place to discuss this, but as part of working on #44868, I’d like to verify the precise guarantees made by Borrow. My feeling is that the set of traits for which, if they are implemented, both types must exhibit identical behavior, is larger than just the comparison traits.

In other words: Has Borrow been devised exclusively for collections or is there wider use cases?

@partim
Copy link
Contributor

partim commented Nov 27, 2017

Thinking about this some more: The most strict guarantee would probably be: ‘If T: Borrow<U>, then for every trait that is implemented by both T and U, every trait method that takes a &self argument must exhibit the exact same behavior for both T and U.’ A similar guarantee should then be stated for BorrowMut with &mut self.

While not stated currently in the documentation, this is perhaps what is intended?

@mikeyhew
Copy link
Contributor

mikeyhew commented Dec 3, 2017

I'm a bit troubled by @aturon's comment about Borrow and Hash and Ord:

Borrow is used by data structures like HashMap and BTreeMap which assume that the result of hashing or ordering based on owned and borrowed values is the same.

I was surprised to hear that types implementing Borrow<T> have to hash and be ordered the same way as T. I see now that it is mentioned in the documentation for Borrow, but it seems like a restriction that is unrelated to the concept of borrowing data, and if HashMap and BTreeMap require those invariants, they should use separate marker traits for them.

I realize it's not possible to add another trait bound to HashMap and BTreeMap at this point, and even if it was, it might not be desirable. It's just that this hurts the Borrow trait by making it less general, and seems prone to error by people like me who implement Borrow for their type without noticing that restriction in the documentation. The latter problem could be addressed by making the following sentence stand out more in the docs:

If you are implementing Borrow and both Self and Borrowed implement Hash, Eq, and/or Ord, they must produce the same result.

@partim
Copy link
Contributor

partim commented Dec 5, 2017

@mikeyhew If you don’t want to make the trait-related guarantees, you probably should implement AsRef. Borrow really should only ever be implemented by smart pointers or other types that manage data without changing its behavior at all. I agree that the naming is a bit unfortunate (though, AsRef is at least a tiny bit shorter), but we are stuck with that.

I tried to make the distinction more clear in the proposed new text in #46518.

@mikeyhew
Copy link
Contributor

mikeyhew commented Dec 6, 2017

@partim AsRef<T> isn't implemented for T though, which doesn't matter when it's used for a function argument that you are going to immediately convert to a reference, but does when you are storing it in some sort of data structure, which isn't necessarily a HashMap or BTreeMap. I mean, there might not be many types that would implement Borrow but hash differently: the only case that I can think of is if you have a struct with two fields that implements Borrow for each, and I'm not sure how useful that is in practice.

kennytm added a commit to kennytm/rust that referenced this issue Mar 19, 2018
Improve documentation for Borrow

This is the first step in improving the documentation for all the reference conversion traits. It proposes new text for the trait documentation of `Borrow`. Since I feel it is a somewhat radical rewrite and includes a stricter contract for `Borrow` then the previous text—namely that *all* shared traits need to behave the same, not just a select few—, I wanted to get some feedback before continuing.

Apart from the ‘normative’ description, the new text also includes a fairly extensive explanation of how the trait is used in the examples section. I included it because every time I look at how `HashMap` uses the trait, I need to think for a while as the use is a bit twisted. So, I thought having this thinking written down as part of the trait itself might be useful. One could argue that this should go into The Book, and, while I really like having everything important in the docs, I can see the text moved there, too.

So, before I move on: is this new text any good? Do we feel it is correct, useful, comprehensive, and understandable?

(This PR is in response to rust-lang#44868 and rust-lang#24140.)
@leoyvens
Copy link
Contributor

leoyvens commented May 2, 2018

Triage: Now that #46518 has landed, can this be closed or at least de-prioritized?

@steveklabnik
Copy link
Member

Yes, closing in favor of that. If there are still improvements to be made, please open new issues!

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 C-enhancement Category: An issue proposing an enhancement or a PR with one. P-high High priority
Projects
None yet
Development

No branches or pull requests

10 participants