-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
libs: Add borrow module, deprecate _equiv and friends #18910
Conversation
Note that the design here is somewhat different than the one in the RFC. This was necessary in order to overcome some language limitations: using This will be noted in the tracking issue, and made part of a general amendment to the RFC with implementation tweaks. |
Also, the deprecation of |
Nifty. |
6c23d67
to
c0e7a32
Compare
Another note: the RFC did not mention generalizing the indexing notation using the borrow traits, but of course that only makes sense. This PR does that as well. (Thanks @nikomatsakis for pointing out that it should.) |
pub fn get(&self, key: &K) -> Option<&V> { | ||
tree_find_with(&self.root, |k2| key.cmp(k2)) | ||
pub fn get<Sized? Q>(&self, key: &Q) -> Option<&V> | ||
where Q: BorrowFrom<K> + Ord { |
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 an interesting stylistic precedent to line up the where
and fn
keywords, I would have expected the alignment of where
to match the alignment of ->
if it came on the next line which I thought was the (
in the argument list.
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.
Yeah, I know :-) generally we need to actually firm up all our style conventions, but that's of course lower priority than API issues. I'm happy to go with your recommended style here.
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.
Personally I'd just do a double-indent, like a normal line continuation.
Is there a reason you chose |
} | ||
|
||
#[unstable = "trait is unstable"] | ||
impl<T> BorrowFromMut<Vec<T>> for [T] { |
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.
Should these be in vec.rs? Genuinely unsure.
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.
Since the BorrowFrom
and BorrowFromMut
traits are in libcore, they cannot implement for Vec
there. Sincce these implementations can't be anywhere else, surely this is the right place?
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.
They could be in the vec
module in libcollections
. I put them here because they are being applied to the slice type, and I feel like putting impls in the module defining their Self
type is a good default.
@aturon it occurs to me that we may be able to implement |
//! | ||
//! Some types make it possible to go from borrowed to owned, usually by | ||
//! implementing the `Clone` trait. But `Clone` works only for going from `&T` | ||
//! to `T`. The `ToOwned` trait generalizes `Clone` to contruct owned 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.
s/contruct/construct
@alexcrichton Note the comments above re: differences from the RFC. |
Yes! That's very much the plan. Note that the blanket impl implicitly requires sized types, which is enough to avoid the overlap. |
fn borrow_from(owned: &T) -> &T { owned } | ||
} | ||
|
||
impl BorrowFrom<&'static str> for 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.
What is this necessary for?
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 allows you to use things like HashMap<&'static str, T>
effectively. However, I don't see any reason to not generalize this to impl<'a, Sized? T> BorrowFrom<&'a T> for T
.
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.
Note: there was an issue a while ago (that I just spent far too long failing to find) about the fact that there was no good way to use data structures keyed on reference types like &'static str
.
\o/ This is wonderful! 😍 |
fn borrow_from_mut(owned: &mut Owned) -> &mut Self; | ||
} | ||
|
||
impl<T: Sized> BorrowFrom<T> for T { |
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 know that the RFC said to use T: Sized
here to prevent overlap with the other impls, but now that we have multidispatch this shouldn't be a problem, right?
You seem to have forgotten btree. |
Possible thing to include: tuple of Owned -> tuple of Borrowed impls. |
@@ -1045,7 +997,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { | |||
/// assert_eq!(map.get(&2), None); | |||
/// ``` | |||
#[unstable = "matches collection reform specification, waiting for dust to settle"] | |||
pub fn get(&self, k: &K) -> Option<&V> { | |||
pub fn get<Sized? Q>(&self, k: &Q) -> Option<&V> where Q: Hash<S> + Eq + BorrowFrom<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.
If I understand correctly, this assumes that the implementations of Hash
and Eq
for Q
and K
are compatible, right? That is, for any k1
and k2
of type K
:
let q1: Q = BorrowFrom::borrow_from(k1);
let q2: Q = BorrowFrom::borrow_from(k2);
assert_eq!(make_hash(q1), make_hash(k1));
assert_eq!(q1 == q2, k1 == k2);
That invariant unfortunately doesn’t seem like it can be enforced by the type system, but maybe it should be documented?
The problem was the same with Equiv
. We ran into it in Servo when trying to make hashing of interned strings O(1)
by only hashing the internal u64
.
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.
Yes, this was a tradeoff brought up in the RFC. It's possible to enforce this invariant, but at some loss in flexibility: it would force you to consistently use a single kind of borrowed value for lookups.
Agreed about documentation.
b55a578
to
9c9615a
Compare
Pushed a new version; I believe I've addressed all comments. |
(C++14 provides methods like |
Any way Cow could not be done through deref(mut)? It was pointed out on IRC that this would be Rust's first "costly" use of those traits, which at least to me seems like it's sufficient to negate the syntactic simplicity of magic. |
@thestinger with rust-lang/rfcs#439 we'll have a trait definition that looks like: pub trait Ord<Rhs = Self>: Eq<Rhs> + PartialOrd<Rhs> {
fn cmp(&self, other: &Rhs) -> Ordering;
} in which case I think we can leverage the impl<K, V> TreeMap<K, V> {
fn get<Q>(&self, q: &Q) -> Option<&V> where K: Ord<Q> { /* ... */ }
}
impl<T, U: BorrowFrom<T>> Ord<U> for T { /* ... */ } I'm not entirely sure how much coherence would disallow, but it seems like it should be possible. |
@pythonesque yes, you're right, supporting |
9c9615a
to
bbc53d7
Compare
@pythonesque I've removed the @thestinger I reinstated the @alexcrichton re-r? |
@aturon: Thanks, I'm in full agreement with this pull request then. |
bbc53d7
to
2174e57
Compare
2174e57
to
7cf8589
Compare
7cf8589
to
1d88e73
Compare
168d74a
to
a8a8b23
Compare
/// A generalization of Clone to borrowed data. | ||
pub trait ToOwned<Owned> for Sized?: BorrowFrom<Owned> { | ||
/// Create owned data from borrowed data, usually by copying. | ||
fn to_owned(&self) -> 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.
Why to_owned
must takes a reference?
I think that HashMap::entry()
can take a ToOwned<K>
as key, and create a owned object if needed. But to have that to work, we should have to_owned
taking self
to allow T: ToOwned<T>
to just move.
Example: http://is.gd/YORUei
I didn't tried to modify this pull request with this change, so maybe there is problem in Cow with this change.
Following [the collections reform RFC](rust-lang/rfcs#235), this commit adds a new `borrow` module to libcore. The module contains traits for borrowing data (`BorrowFrom` and `BorrowFromMut`), generalized cloning (`ToOwned`), and a clone-on-write smartpointer (`Cow`).
This commit generalizes methods like `get` and `remove` for `TreeMap` and `TreeSet` to use the new `std::borrow` infrastructure. [breaking-change]
This commit deprecates the `_equiv` family of methods on `HashMap` and `HashSet` by instead generalizing the "normal" methods like `get` and `remove` to use the new `std::borrow` infrastructure. [breaking-change]
This commit handles the fallout from deprecating `_with` and `_equiv` methods.
Generalizes the BTree-based collections to use the new BorrowFrom infrastructure for more flexible lookups and removals.
375b205
to
46be8eb
Compare
Following the collections reform RFC, this PR:
borrow
module to libcore. The module contains traits for borrowing data (BorrowFrom
andBorrowFromMut
), generalized cloning (ToOwned
), and a clone-on-write smartpointer (Cow
)._equiv
family of methods onHashMap
andHashSet
by instead generalizing the "normal" methods likeget
andremove
to use the newstd::borrow
infrastructure.TreeMap
,TreeSet
,BTreeMap
andBTreeSet
to use the newstd::borrow
infrastructure for lookups.[breaking-change]