-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Improve documentation for Borrow #46518
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @Kimundi (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
src/libcore/borrow.rs
Outdated
@@ -12,26 +12,143 @@ | |||
|
|||
#![stable(feature = "rust1", since = "1.0.0")] | |||
|
|||
/// A trait for borrowing data. | |||
// impl Borrow<str> for String |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these lines seem left in by accident?
src/libcore/borrow.rs
Outdated
/// | ||
/// As a consequence, this trait should only be implemented for types managing | ||
/// a value of another type without modifying its behavior. Examples are | ||
/// smart pointers such as [`Box`] or [`Rc`] as well the owned version of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these three types should have their <T>
s
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
Before I commit that: AsRef
and Borrow
don’t need theirs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i always forget that those don't have parameters, but generally, they're part of the type's "proper name", so they get paramters. So like "vector" doesn't need one, but Vec<T>
should have one, that kind of thing
src/libcore/borrow.rs
Outdated
/// | ||
/// # Examples | ||
/// | ||
/// As a data collection, [`HashMap`] owns both keys and values. If the key’s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<K, V>
I defiantly want @rust-lang/libs to sign off on this. Thank you for taking it on! This is really important work. |
src/libcore/borrow.rs
Outdated
// impl<T> Borrow<T> for Arc<T> | ||
// impl<K> HashSet<K> { fn get<Q>(&self, q: &Q) where K: Borrow<Q> } | ||
|
||
/// A trait identifying how borrowed data behaves. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this sentence confusing. How about:
"Implementors of Borrow<Borrowed>
can be borrowed as Borrowed
."
Now, that is a lot of borrowing, so perhaps the following reads better?
"Implementors of Borrow<T>
can be borrowed as T
."
Just an idea. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is true for AsRef<T>
, too, though. I don’t like the sentence either, but if at all possible in one sentence, the summary should express those strict guarantees implied by Borrow<T>
. Or at least be enough of a tease that people will read on.
src/libcore/borrow.rs
Outdated
/// key’s data. For instance, if the key is a string, then it is likely | ||
/// stored with the hash map as a [`String`], while it should be possible | ||
/// to search using a [`&str`][`str`]. Thus, `insert` needs to operate on a | ||
/// string while `get` needs to be able to use a `&str`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string -> String
?
src/libcore/borrow.rs
Outdated
/// | ||
/// impl<K, V> HashMap<K, V> { | ||
/// pub fn insert(&self, key: K, value: V) -> Option<V> | ||
/// where K: Hash + Eq |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's format this example code using the new style:
https://github.com/rust-lang-nursery/rustfmt/blob/master/tests/target/where-clause-rfc.rs
src/libcore/borrow.rs
Outdated
/// } | ||
/// ``` | ||
/// | ||
/// The entire hash map is generic over the stored type for the key, `K`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about just "over the key type, K
"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to already hint at the difference between K
and Q
here, i.e, that K
is in fact some sort of owned type for the key. I’ll try and rewrite it in a way that expresses this more clearly.
src/libcore/borrow.rs
Outdated
/// smart pointers such as [`Box`] or [`Rc`] as well the owned version of | ||
/// slices such as [`Vec`]. | ||
/// | ||
/// A relaxed version that allows providing a reference to some other type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
providing -> converting?
src/libcore/borrow.rs
Outdated
/// ``` | ||
/// | ||
/// The entire hash map is generic over the stored type for the key, `K`. | ||
/// When inserting a value, the map is given such a `K` and needs to find |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"inserting a key-value pair,"
src/libcore/borrow.rs
Outdated
/// The entire hash map is generic over the stored type for the key, `K`. | ||
/// When inserting a value, the map is given such a `K` and needs to find | ||
/// the correct hash bucket and check if the key is already present based | ||
/// on that `K` value. It therefore requires `K: Hash + Eq`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"that K
value" is a bit confusing since it mixes up "value" as in "data" with "value" as in "V
".
src/libcore/borrow.rs
Outdated
/// result as `Q`’s by demanding that `K: Borrow<Q>`. | ||
/// | ||
/// As a consequence, the hash map breaks if a `K` wrapping a `Q` value | ||
/// produces a different hash than `Q`. For instance, image you have a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
image -> imagine
src/libcore/borrow.rs
Outdated
/// | ||
/// As a consequence, the hash map breaks if a `K` wrapping a `Q` value | ||
/// produces a different hash than `Q`. For instance, image you have a | ||
/// type that wraps a string but compares ASCII letters case-insensitive: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
case-insensitively?
as case-insensitive?
src/libcore/borrow.rs
Outdated
/// (a mutable borrow). But types like `Vec<T>` provide additional kinds of | ||
/// borrows: the borrowed slices `&[T]` and `&mut [T]`. | ||
/// Because two equal values need to produce the same hash value, the | ||
/// implementation of `Hash` need to reflect that, too: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need -> needs
As an observer who did not follow any of the discussions leading up to this and always found |
Looks like there are some broken links causing the build to fail?
In the meantime:
Could someone from @rust-lang/libs start the RFC approval process? |
src/libcore/borrow.rs
Outdated
/// ``` | ||
/// | ||
/// The entire hash map is generic over a key type `K`. Because these keys | ||
/// are stored by with the hash map, this type as to own the key’s data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two typos: "stored by with the hash map" and "this type as to own"
src/libcore/borrow.rs
Outdated
/// trait: if `T: Borrow<U>`, then `&U` can be borrowed from `&T`. A given | ||
/// type can be borrowed as multiple different types. In particular, `Vec<T>: | ||
/// Borrow<Vec<T>>` and `Vec<T>: Borrow<[T]>`. | ||
/// As a consequence, this trait should only be implemented for types managing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's reword this sentence or the previous one, consecutive sentences should not both start with "As a consequence".
src/libcore/borrow.rs
Outdated
/// | ||
/// Instead, `get` relies on `Q`’s implementation of `Hash` and uses `Borrow` | ||
/// to indicate that `K`’s implementation of `Hash` must produce the same | ||
/// result as `Q`’s by demanding that `K: Borrow<Q>`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is a little more to it. If this were the end of the story, then a bound Q: Borrow<K>
would work just as well. In fact that would seem to match the previous paragraph better -- Q: Borrow<K>
means you can take the &Q
and borrow a &K
and use K
's Hash function to find the right bucket. Would it be possible to clarify what is going on and why Q: Borrow<K>
is not just as good?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have rewritten the explanation, calling out what Q
really is supposed to be. Is it more clear now?
src/libcore/borrow.rs
Outdated
/// exactly like a reference to `Borrowed`. As a consequence, if a trait is | ||
/// implemented both by `Self` and `Borrowed`, all trait methods that | ||
/// take a `&self` argument must produce the same result in both | ||
/// implementations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is too strong a statement in my opinion. For example the following is probably fine. Is there a way to make a stronger statement than the existing documentation but without applying it to "all trait methods"?
#![feature(get_type_id)]
use std::any::Any;
use std::borrow::Borrow;
fn assert_borrow<Q: ?Sized, K: Borrow<Q>>() {}
fn main() {
assert_borrow::<str, String>();
let str_id = <str as Any>::get_type_id("");
let string_id = <String as Any>::get_type_id(&String::new());
println!("{}", str_id == string_id); // false
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This exception could perhaps be formulated as ‘trait methods that concern the type of a value rather than a value.’ It’s a bit awkward; perhaps there is a better way to formulate this?
Are there any exceptions where the value itself concerned, though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is one that proclaims to involve the "val" of a value.
use std::mem;
trait Layout {
fn size_of_val(&self) -> usize {
mem::size_of_val(self)
}
fn align_of_val(&self) -> usize {
mem::align_of_val(self)
}
}
impl<T: ?Sized> Layout for T {}
fn main() {
println!("size of str: {}", <str as Layout>::size_of_val(""));
println!("align of str: {}", <str as Layout>::align_of_val(""));
println!("size of String: {}", <String as Layout>::size_of_val(&String::new()));
println!("align of String: {}", <String as Layout>::align_of_val(&String::new()));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is one from the standard library that behaves differently between Q and K and the value is concerned.
use std::borrow::Borrow;
fn assert_borrow<Q: ?Sized, K: Borrow<Q>>() {}
fn main() {
assert_borrow::<str, &str>();
// copies the data
let _: String = <str as ToOwned>::to_owned("...");
// does not copy the data
let _: &str = <&str as ToOwned>::to_owned(&"...");
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps distinguish between ‘management of data’ and ‘properties of data’? It is too vague for my liking, but it sounds like there won’t be a hard-fast rule and it will always be a judgment call.
r? @dtolnay |
It may be valuable to try to push the intuition that Borrow is for types that would not be distinct in a less granular type system than Rust's. The Rust approach to ownership and borrowing, its standpoint in the systems programming space, its dedication to performance and low-level programmer control and zero cost abstractions, all necessitate in various ways that we expose multiple representations of certain types. For example we can all think of programming languages in which the distinction between Similar situation for Similar also for |
Absolutely! My plan was to answer with new text, but then Christmas and now (lucky me!) vacation. Sorry for the delays. I’ll try and propose a new summary and introduction soon. |
@partim hi from the release team! The reviewer added a few comments that should be addressed, could you fix them so this PR can be merged? Thanks! |
I was hoping to get some feedback from @dtolnay on my proposals for solving two of the comments before rewriting the text. |
Many thanks for the great feedback, @dtolnay! I would like to discuss the headline for the trait. The current documentation has "A trait for borrowing data" which I think doesn’t convey the purpose of the trait very much. My current headline is "A trait identifying how borrowed data behaves" but that is probably even worse. The current text suggests something along the line of "A trait to signal a specialized representation" but that is clearly to lofty and not a good summary at all. Does anyone have a good idea? On the other hand, maybe it is a good idea to keep the old headline and modify the text to explicitely introduce the term "borrowing as something"? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The most important thing about the headline should be to concisely communicate that the data underlying a given reference, for example a &Vec<T>
, can occasionally be borrowed in a selection of representations, in this case &Vec<T>
and &[T]
. This is sufficient to differentiate Borrow
from Deref
(which exposes a single referent, not a choice of representations) as well as AsRef
(which is not limited to representations of just one conceptual underlying data).
I've decided to bring back the term "borrowing as" since that is what the trait is named. I am now quite happy with the text. I have also rewritten the documentation for I will provide a rewrite of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Thanks for sticking with this. Let's have @rust-lang/docs take another look because this has changed significantly since Steve's last review.
src/libcore/borrow.rs
Outdated
/// [`String`]: ../../std/string/struct.String.html | ||
/// [`borrow`]: #tymethod.borrow | ||
/// | ||
/// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you remove one of these blank lines please?
src/libcore/borrow.rs
Outdated
/// [`Hash`]: ../../std/hash/trait.Hash.html | ||
/// [`HashMap<K, V>`]: ../../std/collections/struct.HashMap.html | ||
/// [`String`]: ../../std/string/struct.String.html | ||
/// [`str`]: ../../std/primitive.str.html | ||
/// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you remove this line please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
two tiny formatting nits, and then r=me
thank you so much again for this PR and sticking with it!
All right, I suggest to start a new PR for AsRef to not delay this one any further. Thank you, everyone, for your feedback and help! |
@bors r+ rollup |
📌 Commit 13d94d6 has been approved by |
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.)
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 forBorrow
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 #44868 and #24140.)