For example:
let mut map : BTreeSet<i32> = BTreeSet::new();
map.insert(1);
map.insert(2);
map.range(&1..&10) // use reference here
.for_each(|i| {
println!("{}", i);
});
let mut map : BTreeSet<String> = BTreeSet::new();
map.insert("aaaaa".to_string());
map.insert("aaaab".to_string());
map.insert("abbbb".to_string());
map.insert("zzzzz".to_string());
println!("{:?}", map);
map.range("aaaab".."zzzzz"); // do not compile, don't know how to code.
In this case, I must use map.range_mut("aaaab".to_string().."zzzzz".to_string()), but to_string
is expensive.
No, because the BTree
will break if the relative order of the strings changes while they are in the set. You'll need to remove them and then re-add the modified ones.
Then why map.range(&1..&10)
works?
You (usually) can't use &
references to change the ordering, so there's no problem giving them out while the items are still in the set.
Wait a second, range_mut
is a typo, I want to use range
. Sorry for that. Does the answer still valid?
I'm not intent to change orders in the set, just want to iterator on a range. 
1 Like
The issue here is that String
implements Borrow<str>
but not Borrow<&str>
. It looks like it's supposed to work, but the ?Sized
bound has been left off of the relevant RangeBounds
implementation. Looking through the list of other implementations, this should work:
use std::ops::Bound::{Included, Excluded};
map.range::<str,_>((Included("aaaab"), Excluded("zzzzz")))
5 Likes
Looks like it’s the correct workaround, judging e.g. by this comment on GitHub.
2 Likes
It works, but checked the code and didn't find out how it works... What's the magic here?
If you're doing this a lot, you can define a helper function:
use std::ops::RangeBounds;
fn str_range<'a>(r:impl RangeBounds<&'a str>)->impl 'a + RangeBounds<str> {
(r.start_bound().cloned(), r.end_bound().cloned())
}
// ....
map.range(str_range("aaaab".."zzzzz"))
Might as well generalize to
fn ref_range<'a, T: ?Sized + 'a>(r: impl RangeBounds<&'a T>) -> impl 'a + RangeBounds<T>
2 Likes
The method wants
pub fn range<T, R>(&self, range: R) -> Range<'_, K, V>
where
T: Ord + ?Sized,
K: Borrow<T> + Ord,
R: RangeBounds<T>,
K = String
and the Borrow
relationship you want to use here is String: Borrow<str>
, so you have T = str
.
Then for the the actual range argument with type R
, you need R: RangeBounds<str>
. For "a".."z"
, the resulting type is R = Range<&str>
, for example.
The first implementation in this list allows T: ?Sized
, but the others (except for ..
) don't. So this is fulfilled:
(Bound<&str>, Bound<&str>): RangeBounds<str>
But due to str
being unsized, this is not fulfilled:
Range<&str>: RangeBounds<str>
2 Likes
Here's a more ergonomic work-around IMO.
struct R<'a>(Range<&'a str>);
impl RangeBounds<str> for R<'_> { /* ... */ }
map.range(R("aaaab".."zzzzz"));
Could be generalized to R<'a, T>
, implement for T = [the various Range* types]
.
3 Likes