In the following rust code I am trying to change the contents of an array:
let mut example_state = [[0;8]; 2];
for mut i in example_state.iter() {
let mut k = 0;
for j in i.iter(){
i[k] = 9u8;
k +=1
}
}
However I get the error message:
src/main.rs:18:13: 18:23 error: cannot assign to immutable indexed content `i[..]`
src/main.rs:18 i[k] = 9u8;
which I'm confused by because I am defining i to be mut and example_state is also mutable.
I also don't know if this is the best way to change the contents of an array - do I need the counter k or can I simply use the iterator j in some way?
UPDATE:
So I found that this block of code works:
let mut example_state = [[n;8]; 2];
for i in example_state.iter_mut() {
for j in i.iter_mut(){
*j = 9u8;
}
}
but I would appreciate some explanation of what the difference is between them, iter_mut doesn't throw up much on Google.
Let's look at the signatures of the two methods, iter and iter_mut:
fn iter(&self) -> Iter<T>;
fn iter_mut(&mut self) -> IterMut<T>;
And the structs they return, Iter and IterMut, specifically the implementation of Iterator:
// Iter
type Item = &'a T
// IterMut
type Item = &'a mut T
These are associated types, but basically in this case, they specify what the return type of calling Iterator::next. When you used iter, even though it was on a mutable variable, you were asking for an iterator to immutable references to a type T (&T). That's why you weren't able to mutate them!
When you switched to iter_mut, the return type of Iterator::next is &mut T, a mutable reference to a type T. You are allowed to set these values!
As an aside, your question used arrays, not slices, but there aren't documentation links for arrays (that I could find quickly), and slices are close enough to arrays so I used them for this explanation.
There are two orthogonal concepts going on here:
Whether the reference itself is mutable. That's the difference between i and mut i.
Whether the data it points to is mutable. That's the difference between .iter()/&T and .iter_mut()/&mut T.
If you use C, this distinction should be familiar. Your initial code creates mutable references to immutable data, or const char * in C. So while you can assign to the reference itself (i = ...), you can't modify the data it points to (*i = ...). That's why the compiler stops you.
On the other hand, your fixed code creates immutable references to mutable data. That's char * const in C. This doesn't let you assign to the reference itself, but it does let you modify the underlying array, so it compiles as expected.
So why does Rust have a separate .iter() and .iter_mut()? Because in Rust, while you can take as many &T to a structure as you want, you can only modify it through a single &mut T. In other words, mutable references are unique and never alias.
Having both .iter() and .iter_mut() gives you a choice. On one hand, you can have any number of immutable iterators in scope at once, all pointing to the same array. Here's a silly example that iterates forwards and backwards at the same time:
for i, j in array.iter().zip(array.iter().rev()) {
println!("{} {}", i, j);
}
But if you want a mutable iterator, you have to guarantee the references never alias. So this won't work:
// Won't compile
for i, j in array.iter_mut().zip(array.iter_mut().rev()) {
println!("{} {}", i, j);
}
because the compiler can't guarantee i and j don't point to the same location in memory.
Related
This question popped into my head (while I wasn't programming), and it actually made me question a lot of things about programming (like in C++, C#, Rust, in particular).
I want to point out, I'm aware there is a similar question on this issue:
Cannot borrow as mutable because it is also borrowed as immutable.
But I believe this question is aiming at a particular situation; a sub-problem. And I want to better understand how to resolve a thing like this in Rust.
The "thing" that I realised recently was that: "If I have a pointer/reference to an element in a dynamic array, and then I add an element, causing the array to expand and reallocate, that would break the pointer. Therefore, I need a special refererence that will always point to the same element even if it re-allocates".
This made me start thinking differently about a lot of things. But outside of that, I am aware that this problem is trivial to experienced c++ programmers. I have simply not come across this situation in my experiences, unfortunately.
So I wanted to see if Rust either had an existing 'special type' for this type of issue, and if not, what would happen if I made my own (for testing). The idea is that this "special pointer" would simply be a pointer to the Vector (List) itself, but also have a i32 field for the index; so it's all bundled under 1 variable that can be 'dereferenced' whenever you need.
Note: "VecPtr" is meant to be a immutable reference.
struct VecPtr<'a, T> {
vec: &'a Vec<T>,
index: usize
}
impl<T: Copy> VecPtr<'_, T> {
pub fn value(&self) -> T {
return self.vec[self.index];
}
}
fn main() {
let mut v = Vec::<i32>::with_capacity(6);
v.push(3);
v.push(1);
v.push(4);
v.push(1);
let r = VecPtr {vec: &v,index: 2};
let n = r.value();
println!("{}",n);
v.push(5); // error!
v.push(9); // error!
v.push(6); // re-allocation triggered // also error!
let n2 = r.value();
println!("{}",n2);
return;
}
So the above example code is showing that you can't have an existing immutable reference while also trying to have a mutable reference at the same time. good!
From what I've read from the other StackOverflow question, one of the reasons for the compiler error is that the Vector could re-allocate it's internal array at any time when it is calling "push". Which would invalidate all references to the internal array.
Which makes 100% sense. So as a programmer, you may desire to still have references to the array, but they are designed to be a bit more safer. Instead of a direct pointer to the internal array, you just have a pointer to the vector itself in question, and include an i32 index so you know the element you are looking at. Which means the dangling pointer issue that would occur at v.push(6); shouldn't happen any more. But yet the compiler still complains about the same issue. Which I understand.
I suppose it's still concerned about the reference to the vector itself, not the internals. Which makes things a bit confusing. Because there are different pointers here that the compiler is looking at and trying to protect. But to be honest, in the example code, the pointer to vec itself looks totally fine. That reference doesn't change at all (and it shouldn't, from what I can tell).
So my question is, is there a practice at which you can tell the compiler your intentions with certain references? So the compiler knows there isn't an issue (other than the unsafe keyword).
Or alternatively, is there a better way to do what I'm trying to do in the example code?
After some more research
It looks like one solution here would be to use reference counting Rc<T>, but I'm not sure that's 100% it.
I would normally not ask this question due to there being a similar existing question, but this one (I think) is investigating a slightly different situation, where someone (or me) would try to resolve an unsafe reference situation, but the compiler still insists there is an issue.
I guess the question comes down to this: would you find this acceptable?
fn main() {
let mut v = Vec::<i32>::with_capacity(6);
v.push(3);
v.push(1);
v.push(4);
v.push(1);
let r = VecPtr { vec: &v, index: 2 };
let n = r.value();
println!("{}",n);
v[2] = -1;
let n2 = r.value(); // This returned 4 just three lines ago and I was
// promised it wouldn't change! Now it's -1.
println!("{}",n2);
}
Or this
fn main() {
let mut v = Vec::<i32>::with_capacity(6);
v.push(3);
v.push(1);
v.push(4);
v.push(1);
let r = VecPtr { vec: &v, index: 2 };
let n = r.value();
println!("{}",n);
v.clear();
let n2 = r.value(); // This exact same thing that worked three lines ago will now panic.
println!("{}",n2);
}
Or, worst of all:
fn main() {
let mut v = Vec::<i32>::with_capacity(6);
v.push(3);
v.push(1);
v.push(4);
v.push(1);
let r = VecPtr { vec: &v, index: 2 };
let n = r.value();
println!("{}",n);
drop(v);
let n2 = r.value(); // Now you do actually have a dangling pointer.
println!("{}",n2);
}
Rust's answer is an emphatic "no" and that is enforced in the type system. It's not just about the unsoundness of dereferencing dangling pointers, it's a core design decision.
Can you tell the compiler your intentions with certain references? Yes! You can tell the compiler whether you want to share your reference, or whether you want to mutate through it. In your case, you've told the compiler that you want to share it. Which means you're not allowed to mutate it anymore. And as the examples above show, for good reason.
For the sake of this, the borrow checker has no notion of the stack or the heap, it doesn't know what types allocate and which don't, or when a Vec resizes. It only knows and cares about moving values and borrowing references: whether they're shared or mutable and for how long they live.
Now, if you want to make your structure work, Rust offers you some possibilities: One of those is RefCell. A RefCell allows you to borrow a mutable reference from an immutable one at the expense of runtime checking that nothing is aliased incorrectly. This together with an Rc can make your VecPtr:
use std::cell::RefCell;
use std::rc::Rc;
struct VecPtr<T> {
vec: Rc<RefCell<Vec<T>>>,
index: usize,
}
impl<T: Copy> VecPtr<T> {
pub fn value(&self) -> T {
return self.vec.borrow()[self.index];
}
}
fn main() {
let v = Rc::new(RefCell::new(Vec::<i32>::with_capacity(6)));
{
let mut v = v.borrow_mut();
v.push(3);
v.push(1);
v.push(4);
v.push(1);
}
let r = VecPtr {
vec: Rc::clone(&v),
index: 2,
};
let n = r.value();
println!("{}", n);
{
let mut v = v.borrow_mut();
v.push(5);
v.push(9);
v.push(6);
}
let n2 = r.value();
println!("{}", n2);
}
I'll leave it to you to look into how RefCell works.
This question already has answers here:
How to get mutable references to two array elements at the same time?
(8 answers)
Closed 8 months ago.
I'm solving a problem from Leetcode and encountered the fact that Rust won't let me execute it efficiently. What am I doing wrong? I know about the book article about references and borrowing and would like to know how to solve this problem despite the peculiarities of the language.
I am trying to create one reference for a vec that should change and another for a vec that will not change. Rust won't let me do that. The program works, but only when using .clone(), which will be very slow and not necessary (last_row does not change anywhere, only the values are derived from there).
Here is the working code:
use std::cmp;
fn minimum_total(mut triangle: Vec<Vec<i32>>) -> i32 {
for i in (0..triangle.len()-1).rev() { // from penultimate (last - 1) to first
let last_row = & triangle[i+1].clone();
let current_row = &mut triangle[i];
for j in 0..current_row.len() {
current_row[j] = cmp::min(last_row[j], last_row[j+1]) + current_row[j];
}
}
triangle[0][0]
}
fn main() {
println!("{}", minimum_total(vec![vec![2],vec![3,4],vec![6,5,7],vec![4,1,8,3]]));
}
As you can see, I used .clone() to fix the borrow checker errors that show up when you try to write a program using references:
use std::cmp;
fn minimum_total(mut triangle: Vec<Vec<i32>>) -> i32 {
for i in (0..triangle.len()-1).rev() { // from penultimate (last - 1) to first
let current_row = &mut triangle[i];
let last_row = &triangle[i+1];
for j in 0..current_row.len() {
current_row[j] = cmp::min(last_row[j], last_row[j+1]) + current_row[j];
}
}
triangle[0][0]
}
fn main() {
println!("{}", minimum_total(vec![vec![2],vec![3,4],vec![6,5,7],vec![4,1,8,3]]));
}
Terminal:
error[E0502]: cannot borrow `triangle` as immutable because it is also borrowed as mutable
--> src\main.rs:6:25
|
5 | let current_row = &mut triangle[i];
| -------- mutable borrow occurs here
6 | let last_row = &triangle[i+1];
| ^^^^^^^^ immutable borrow occurs here
7 | for j in 0..current_row.len() {
| ----------------- mutable borrow later used here
For more information about this error, try `rustc --explain E0502`.
However, when trying to write a program poorly everything works without any problems:
use std::cmp;
fn minimum_total(mut triangle: Vec<Vec<i32>>) -> i32 {
for i in (0..triangle.len()-1).rev() { // from penultimate (last - 1) to first
for j in 0..triangle[i].len() {
triangle[i][j] = cmp::min(triangle[i+1][j], triangle[i+1][j+1]) + triangle[i][j];
}
}
triangle[0][0]
}
fn main() {
println!("{}", minimum_total(vec![vec![2],vec![3,4],vec![6,5,7],vec![4,1,8,3]]));
}
You can accomplish this via the split_at_mut() method, which comes from the primitive slice type (which Vec auto-derefs to). This method allows you to safely take a mutable slice and split it into two mutable slices at a given index, since it's guaranteed that the two slices won't overlap. (Note this is zero-copy, as slices are just fat pointers borrowing an existing contiguous sequence.)
The two slices then are independent for the purposes of borrow checking, so you can borrow mutably from both slices at the same time (or, in your case, mutably from one and immutably from the other).
use std::cmp;
fn minimum_total(mut triangle: Vec<Vec<i32>>) -> i32 {
for i in (0..triangle.len()-1).rev() { // from penultimate (last - 1) to first
let (left, right) = triangle.split_at_mut(i + 1);
let current_row = left.last_mut().unwrap();
let last_row = right.first().unwrap();
for j in 0..current_row.len() {
current_row[j] = cmp::min(last_row[j], last_row[j+1]) + current_row[j];
}
}
triangle[0][0]
}
fn main() {
println!("{}", minimum_total(vec![vec![2],vec![3,4],vec![6,5,7],vec![4,1,8,3]]));
}
Yes, that's the thing with Rust -- you have to code in a way that the compiler can tell it is safe. Sometimes that requires a bit of thought, but often in the end you have code that is cleaner than you would have written otherwise.
Imagine having a function that could walk through items two at a time, calling a function you specify on them, with the first being immutable, and the second being mutable. Call it pairs_mut, and calling it with function f on a,b,c,d it would result in calls to f(&a, &mut b), f(&b, &mut c), and f(&c, &mut d). A non-generic version is not that hard to write. I am hesitant to put the code here because you are trying to learn from the exercise.
NOTE: I suspect that such a facility (or perhaps something more general) exists somewhere in the Rust ecosystem, but I have looked in Iterator and the itertools crate and didn't find anything. If you know of an existing facility like this, please share a link in a comment. Otherwise perhaps I should try to get something added to itertools.
Now given pairs_mut, I hope you can see that minimum_total could run it on triangle.rev() and do that bit of dynamic programming to come up with the minimum sum. Let me know if you want me to put some actual code here, but I encourage you to try it yourself first.
use std::cell::RefCell;
use std::rc::Weak;
struct Elem {
attached_elem: Weak<RefCell<Elem>>,
value: i32,
}
impl Elem {
fn borrow_mut_attached_elem(&self) -> &mut Elem {
//what should this line be?
self.attached_elem.upgrade().unwrap().borrow_mut()
}
}
fn main(){}
I have tried some similar other lines but nothing has worked so far, even the experimental cell_leak feature for RefMut.
I don't mind changing the signature of the function I just want to reduce the overhead of getting a mutable reference to the attached_elem from an Elem.
what should this line be?
There is nothing you can put in that line to (safely) satisfy the function signature - and that's for good reason. While RefCell does allow obtaining &mut T from a RefCell<T> (that's why it exists), it must guarantee that only one mutable reference exist at a time. It does so by only providing a temporary reference whose lifetime is tied to the RefMut<T> wrapper. Once the wrapper is dropped, the value is marked as no longer borrowed, so the reference must not outlive it.
If Rust were to allow you to return a naked &mut Elem, you'd be able to use the reference after the RefCell has ceased being marked as borrowed. In that case, what's to stop you from calling borrow_mut_attached_elem() again, and obtain a second mutable reference to the same Elem?
So you'll definitely need to change the signature. If you just need to give outside code temporary access to &mut Elem, the easiest way is to accept a closure that will receive it. For example:
fn with_attached_elem<R>(&self, f: impl FnOnce(&mut Elem) -> R) -> R {
let rc = self.attached_elem.upgrade().unwrap();
let retval = f(&mut *rc.borrow_mut());
retval
}
You'd call it to do something with the element, e.g.:
elem.with_attached_elem(|e| e.value += 1);
with_attached_elem takes care to return the value returned by the closure, allowing you to collect data from &mut Elem and propagate it to the caller. For example, to pick up the value of the attached element you could use:
let value = elem.with_attached_elem(|e| e.value);
Consider this code (Rust Playground):
#[derive(Clone, Copy, Debug)]
struct X(i32);
impl std::ops::AddAssign for X {
fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0;
}
}
fn main() {
let mut ary_i32 = [1_i32; 2];
ary_i32[0] += ary_i32[1]; // OK
let mut ary_x = [X(1); 2];
ary_x[0] += ary_x[1]; // OK
let mut vec_i32 = vec![1_i32; 2];
vec_i32[0] += vec_i32[1]; // OK
let mut vec_x = vec![X(1); 2];
vec_x[0] += vec_x[1]; // error[E0502]: cannot borrow `vec_x` as immutable because it is also borrowed as mutable
}
Why I get E0502 only on vec_x line?
I could not understand why only the operations for ary_x and vec_i32 are permitted. Does borrow checker treat builtin types (i32, array) specially?
I researched some resources and read MIR of my code, and managed to understand what is going on.
The comment by #trentcl will be the best answer.
I write the details as possible.
For array, Index and IndexMut traits are not used and compiler directly manipulates array elements (you can see this with MIR). So, borrowing problem does not exist here.
Explanating for Vec, rustc guide is useful.
First, Two-phase borrow is not applied to vec_foo[0] += vec_foo[1] statement.
And, the difference between i32 and X is caused by operator lowering.
Basically, statements like vec_user_defined[0] += vec_user_defined[1] are converted to function calls like add_assign(index_mut(...), *index(...)), and function arguments are evaluated from left to right. So, index_mut() borrows x mutably and index() tries to borrow x, and fails.
But for builtin types like i32, compound assignment operator is not converted to function call, and rhs is evaluated before lhs (you can see index() is called before index_mut() with MIR). So, for builtin types, vec_builtin[0] += vec_builtin[1] works.
I know these things from lo48576's article (Japanese).
I considered some workarounds:
Just use an intermediate variable as #sebpuetz said.
Convert Vec to slice as #trentcl said. But this doesn't work well for multidimensional Vec.
Write some macro to automatically introduce an intermediate variable. I found rhs_first_assign crate does such works.
Rust arrays live on the stack, are predictably sized, and therefore have stronger borrow checker guarantees. Vectors are smart pointers on the stack pointing at data that can grow and shrink on the Heap. Because the final example uses the Vector type, the borrow checker considers the entire Vector as a single mutably borrowed object when loading it from the Heap.
As you've observed, the borrow checker can create a mutable reference to a single element to something living on the Stack, whereas it creates a mutable reference to the Vector's smart pointer on the Stack, and then a further mutable reference to the data on the heap. This is why the immutable reference to vec_vec_x[1][1] fails.
As #sebpuetz noted in a comment, you can solve this by first copying an immutable reference to vec_vec_x[1][1], then creating an immutable reference.
An example of how a range gets consumed is:
let coll = 1..10;
for i in coll {
println!("i is {}", &i);
}
println!("coll length is {}", coll.len());
This will fail with
error[E0382]: borrow of moved value: `coll`
--> src/main.rs:6:35
|
2 | let coll = 1..10;
| ---- move occurs because `coll` has type `std::ops::Range<i32>`, which does not implement the `Copy` trait
3 | for i in coll {
| ----
| |
| `coll` moved due to this implicit call to `.into_iter()`
| help: consider borrowing to avoid moving into the for loop: `&coll`
...
6 | println!("coll length is {}", coll.len());
| ^^^^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `coll`
The usual way to fix this is to borrow the coll, but that doesn't work here:
error[E0277]: `&std::ops::Range<{integer}>` is not an iterator
--> src/main.rs:3:14
|
3 | for i in &coll {
| -^^^^
| |
| `&std::ops::Range<{integer}>` is not an iterator
| help: consider removing the leading `&`-reference
|
= help: the trait `std::iter::Iterator` is not implemented for `&std::ops::Range<{integer}>`
= note: required by `std::iter::IntoIterator::into_iter`
Why is that? Why is a borrowed range not an iterator, but the range is? Is it interpreting it differently?
To understand what is happening here it is helpful to understand how for loops work in Rust.
Basically a for loop is a short hand for using an iterator, so:
for item in some_value {
// ...
}
is basically a short-hand for
let mut iterator = some_value.into_iter();
while let Some(item) = iterator.next() {
// ... body of for loop here
}
So we can see that whatever we loop over with the for loop, Rust calls the into_iter method from the IntoIterator trait on. The IntoIterator trait looks (approximately) like this:
trait IntoIterator {
// ...
type IntoIter;
fn into_iter(self) -> Self::IntoIter;
}
So into_iter takes self by value and returns Self::IntoIter which is the type of the iterator. As Rust moves any arguments which are taken by value, the thing .into_iter() was called on is no longer available after the call (or after the for loop). That's why you can't use coll in your first code snippet.
So far so good, but why can we still use a collection if we loop over a reference of it as in the following?
for i in &collection {
// ...
}
// can still use collection here ...
The reason is that for a lot of collections C, the IntoIterator trait is implemented not just for the collection, but also for a shared reference to the collection &C and this implementation produces shared items. (Sometimes it is also implemented for mutable references &mut C which produces mutable references to items).
Now coming back to the example with the Range we can check how it implements IntoIterator.
Looking at the reference docs for Range, Range strangely does not seem to implement IntoIterator directly... but if we check the Blanket Implementations section on doc.rust-lang.org, we can see that every iterator implements the IntoIterator trait (trivially, by just returning itself):
impl<I> IntoIterator for I
where
I: Iterator
How does this help? Well, checking further up (under trait implementations) we see that Range does implement Iterator:
impl<A> Iterator for Range<A>
where
A: Step,
And thus Range does implement IntoIterator via the indirection of Iterator. However, there is no implementation of either Iterator for &Range<A> (this would be impossible) or of IntoIterator for &Range<A>. Therefore, we can use a for loop by passing Range by value, but not by reference.
Why can &Range not implement Iterator? An iterator needs to keep track of "where it is", which requires some kind of mutation, but we cannot mutate a &Range because we only have a shared reference. So this cannot work. (Note that &mut Range can and does implement Iterator - more on this later).
It would technically be possible to implement IntoIterator for &Range as that could produce a new iterator. But the likelihood that this would clash with the blanket iterator implementation of Range would be very high and things would be even more confusing. Besides, a Range is at most two integers and copying this is very cheap, so there is really no big value in implementing IntoIterator for &Range.
If you still want to use collection, you can clone it
for i in coll.clone() { /* ... */ }
// `coll` still available as the for loop used the clone
This brings up another question: If we can clone the range and it is (as claimed above) cheap to copy it, why doesn't Range implement the Copy trait? Then the .into_iter() call would copy the range coll (instead of moving it) and it could still be used after the loop. According to this PR the Copy trait implementation actually existed but was removed because the following was considered a footgun (hat tip to Michael Anderson for pointing this out):
let mut iter = 1..10;
for i in iter {
if i > 2 { break; }
}
// This doesn't work now, but if `Range` implemented copy,
// it would produce `[1,2,3,4,5,6,7,8,9]` instead of
// `[4,5,6,7,8,9]` as might have been expected
let v: Vec<_> = iter.collect();
Also note that &mut Range does implement iterator, so you can do
let mut iter = 1..10;
for i in &mut iter {
if i > 2 { break; }
}
// `[4,5,6,7,8,9]` as expected
let v: Vec<_> = iter.collect();
Finally, for completeness, it might be instructive to see which methods are actually called when we loop over a Range:
for item in 1..10 { /* ... */ }
is translated to
let mut iter = 1..10.into_iter();
// ˆˆˆˆˆˆˆˆˆ--- which into_iter() is this?
while let Some(item) = iter.next() { /* ... */ }
we can make this explicit using qualified method syntax:
let mut iter = std::iter::Iterator::into_iter(1..10);
// it's `Iterator`s method! ------^^^^^^^^^
while let Some(item) = iter.next() { /* ... */ }
Ranges are iterators that modify themselves to generate elements. Therefore, to loop over a range, it is necessary to modify it (or a copy of it, as shown below).
Vectors, on the other hand, are not iterators themselves. .into_iter() is called to create an iterator when a vector is looped over; the vector itself doesn't need to be consumed.
The solution here is to use clone to create a new iterator that can be looped over:
for i in coll.clone() {
println!("i is {}", i);
}
(Incidentally, the println! family of macros take references automatically.)
Let's say you have a vector:
let v = vec![1, 2, 3];
The method iter on Vec returns something that implements the Iterator trait. With a vector, there is also an implementation of the trait Borrow (and BorrowMut), that does not return a &Vec though. Instead, you get a slice &[T]. This slice can then be used to iterate over the elements of the vector.
However, the range (e.g. 1..10) implements IntoIterator already and does not need to be transformed into a slice or some other view into it. Therefore, you can consume the range itself by calling into_iter() (which you do implicitly). Now, it is as if you moved the range into some function and you cannot use your variable coll anymore. The borrowing syntax won't help, since this is only some special functionality of Vec.
In this case, you could construct a Vec from your range (with the collect method), clone the range when iterating over it or get the length before iterating (since getting the length doesn't consume the range itself).
Some references:
https://doc.rust-lang.org/std/vec/struct.Vec.html
https://doc.rust-lang.org/std/primitive.slice.html
https://doc.rust-lang.org/std/ops/struct.Range.html