-
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
Changes from 2 commits
cba5f6b
c4ea700
85e8a9b
fc6c638
7ae7e53
44be054
5bef034
d664b89
13d94d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,26 +12,143 @@ | |
|
||
#![stable(feature = "rust1", since = "1.0.0")] | ||
|
||
/// A trait for borrowing data. | ||
// impl Borrow<str> for String | ||
// 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 commentThe reason will be displayed to describe this comment to others. Learn more. I find this sentence confusing. How about: Now, that is a lot of borrowing, so perhaps the following reads better? Just an idea. :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is true for |
||
/// | ||
/// If a type implements this trait, it signals that a reference to it behaves | ||
/// 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 commentThe 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 commentThe 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 commentThe 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 commentThe 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 commentThe 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. |
||
/// | ||
/// 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 commentThe 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". |
||
/// 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 commentThe reason will be displayed to describe this comment to others. Learn more. these three types should have their There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ack. Before I commit that: There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
/// 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 commentThe reason will be displayed to describe this comment to others. Learn more. providing -> converting? |
||
/// without any further promises is available through [`AsRef`]. | ||
/// | ||
/// When writing generic code, a use of `Borrow` should always be justified | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this guidance apply in the case of match *self {
Borrowed(borrowed) => borrowed,
Owned(ref owned) => owned.borrow(),
}
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternative proposal: ‘When using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That seems reasonable. It may be appropriate to phrase more as justification than as instruction. Instead of instructing a particular use: "here is how you should always do it" or "here is how we suggest that you do it," more like a justification "here is how you will typically find it used and why." |
||
/// by additional trait bounds, making it clear that the two types need to | ||
/// behave identically in a certain context. If the code should merely be | ||
/// able to operate on any type that can produce a reference to a given type, | ||
/// you should use [`AsRef`] instead. | ||
/// | ||
/// The companion trait [`BorrowMut`] provides the same guarantees for | ||
/// mutable references. | ||
/// | ||
/// [`Box`]: ../boxed/struct.Box.html | ||
/// [`Rc`]: ../rc/struct.Rc.html | ||
/// [`Vec`]: ../vec/struct.Vec.html | ||
/// [`AsRef`]: ../convert/trait.AsRef.html | ||
/// [`BorrowMut`]: trait.BorrowMut.html | ||
/// | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you remove one of these blank lines please? |
||
/// # 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 commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
/// actual data is wrapped in a managing type of some kind, it should, | ||
/// however, still be possible to search for a value using a reference to the | ||
/// 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 commentThe reason will be displayed to describe this comment to others. Learn more. string -> |
||
/// | ||
/// Slightly simplified, the relevant parts of `HashMap` look like this: | ||
/// | ||
/// ``` | ||
/// use std::borrow::Borrow; | ||
/// use std::hash::Hash; | ||
/// | ||
/// pub struct HashMap<K, V> { | ||
/// # marker: ::std::marker::PhantomData<(K, V)>, | ||
/// // fields omitted | ||
/// } | ||
/// | ||
/// 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 commentThe reason will be displayed to describe this comment to others. Learn more. Let's format this example code using the new style: |
||
/// { | ||
/// # unimplemented!() | ||
/// // ... | ||
/// } | ||
/// | ||
/// pub fn get<Q>(&self, k: &Q) -> Option<&V> | ||
/// where K: Borrow<Q>, | ||
/// Q: Hash + Eq + ?Sized | ||
/// { | ||
/// # unimplemented!() | ||
/// // ... | ||
/// } | ||
/// } | ||
/// ``` | ||
/// | ||
/// 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 commentThe reason will be displayed to describe this comment to others. Learn more. How about just "over the key type, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to already hint at the difference between |
||
/// 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 commentThe reason will be displayed to describe this comment to others. Learn more. "inserting a key-value pair," |
||
/// 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 commentThe reason will be displayed to describe this comment to others. Learn more. "that |
||
/// | ||
/// In order to search for a value based on the key’s data, the `get` method | ||
/// is generic over some type `Q`. Technically, it needs to convert that `Q` | ||
/// into a `K` in order to use `K`’s [`Hash`] implementation to be able to | ||
/// arrive at the same hash value as during insertion in order to look into | ||
/// the right hash bucket. Since `K` is some kind of owned value, this likely | ||
/// would involve cloning and isn’t really practical. | ||
/// | ||
/// 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 commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have rewritten the explanation, calling out what |
||
/// | ||
/// 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 commentThe reason will be displayed to describe this comment to others. Learn more. image -> imagine |
||
/// type that wraps a string but compares ASCII letters case-insensitive: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. case-insensitively? |
||
/// | ||
/// ``` | ||
/// use std::ascii::AsciiExt; | ||
/// | ||
/// pub struct CIString(String); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In Rust conventionally acronyms count as single words so this would be called |
||
/// | ||
/// impl PartialEq for CIString { | ||
/// fn eq(&self, other: &Self) -> bool { | ||
/// self.0.eq_ignore_ascii_case(&other.0) | ||
/// } | ||
/// } | ||
/// | ||
/// impl Eq for CIString { } | ||
/// ``` | ||
/// | ||
/// In general, there may be several ways to "borrow" a piece of data. The | ||
/// typical ways of borrowing a type `T` are `&T` (a shared borrow) and `&mut T` | ||
/// (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 commentThe reason will be displayed to describe this comment to others. Learn more. need -> needs |
||
/// | ||
/// When writing generic code, it is often desirable to abstract over all ways | ||
/// of borrowing data from a given type. That is the role of the `Borrow` | ||
/// 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]>`. | ||
/// ``` | ||
/// # use std::ascii::AsciiExt; | ||
/// # use std::hash::{Hash, Hasher}; | ||
/// # pub struct CIString(String); | ||
/// impl Hash for CIString { | ||
/// fn hash<H: Hasher>(&self, state: &mut H) { | ||
/// for c in self.0.as_bytes() { | ||
/// c.to_ascii_lowercase().hash(state) | ||
/// } | ||
/// } | ||
/// } | ||
/// ``` | ||
/// | ||
/// If you are implementing `Borrow` and both `Self` and `Borrowed` implement | ||
/// `Hash`, `Eq`, and/or `Ord`, they must produce the same result. | ||
/// Can `CIString` implement `Borrow<str>`? It certainly can provide a | ||
/// reference to a string slice via its contained owned string. But because | ||
/// its `Hash` implementation differs, it cannot fulfill the guarantee for | ||
/// `Borrow` that all common trait implementations must behave the same way | ||
/// and must not, in fact, implement `Borrow<str>`. If it wants to allow | ||
/// others access to the underlying `str`, it can do that via `AsRef<str>` | ||
/// which doesn’t carry any such restrictions. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This case insensitive string example is really clear and helpful! Makes sense why you would not want a |
||
/// | ||
/// `Borrow` is very similar to, but different than, `AsRef`. See | ||
/// [the book][book] for more. | ||
/// [`Hash`]: ../hash/trait.Hash.html | ||
/// [`HashMap`]: ../collections/struct.HashMap.html | ||
/// [`String`]: ../string/struct.String.html | ||
/// [`str`]: ../primitive.str.html | ||
/// | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you remove this line please? |
||
/// [book]: ../../book/first-edition/borrow-and-asref.html | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
pub trait Borrow<Borrowed: ?Sized> { | ||
/// Immutably borrows from an owned value. | ||
|
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?