How to make sense of the following piece of code? I'm new to Rust but have background on C/Haskell and a little bit C++. The only reference I can find is to deref coercions.
fn main() {
let xs: [u32; 4] = [0, 1, 2, 3];
let mut i: u32 = 0;
for x in xs.iter() {
if i > *x { // It looks x is an iterator. Understood.
i = i + x; // no error. (coerced)
//Quote: "Rust will do this as many times
// as possible until the types match."
i = i + *x; // no error (explicit deref)
i += x; // error about u32/&u32 mismatch. Why the magic failed?
i += *x; // no error (explicit deref)
}
}
println!("{}", i);
}
There is no auto-deref or coercion here, i + x works simply because u32 implements both Add<u32> and Add<&u32>. If you check the docs for u32, you'll find the following four trait impls:
impl Add<u32> for u32
impl<'a> Add<u32> for &'a u32
impl<'a> Add<&'a u32> for u32
impl<'a, 'b> Add<&'a u32> for &'b u32
u32 only implements AddAssign<u32> but not AddAssign<&u32> (this is a bug and will be fixed in 1.18 or 1.19 fixing it causes regression which means this impl probably needs to wait for Rust 2.0), so i += x is an error.
impl AddAssign<u32> for u32
//impl<'a> AddAssign<&'a u32> for u32 <-- is missing.
Why does auto-dereferencing not happen? — Auto-deref only happens when it is a receiver i.e. the "self" in a method call foo.bar(). x is not a "self" argument and + is not a method call. So there's no auto-deref here. See What are Rust's exact auto-dereferencing rules? for detail.
Related
How to make sense of the following piece of code? I'm new to Rust but have background on C/Haskell and a little bit C++. The only reference I can find is to deref coercions.
fn main() {
let xs: [u32; 4] = [0, 1, 2, 3];
let mut i: u32 = 0;
for x in xs.iter() {
if i > *x { // It looks x is an iterator. Understood.
i = i + x; // no error. (coerced)
//Quote: "Rust will do this as many times
// as possible until the types match."
i = i + *x; // no error (explicit deref)
i += x; // error about u32/&u32 mismatch. Why the magic failed?
i += *x; // no error (explicit deref)
}
}
println!("{}", i);
}
There is no auto-deref or coercion here, i + x works simply because u32 implements both Add<u32> and Add<&u32>. If you check the docs for u32, you'll find the following four trait impls:
impl Add<u32> for u32
impl<'a> Add<u32> for &'a u32
impl<'a> Add<&'a u32> for u32
impl<'a, 'b> Add<&'a u32> for &'b u32
u32 only implements AddAssign<u32> but not AddAssign<&u32> (this is a bug and will be fixed in 1.18 or 1.19 fixing it causes regression which means this impl probably needs to wait for Rust 2.0), so i += x is an error.
impl AddAssign<u32> for u32
//impl<'a> AddAssign<&'a u32> for u32 <-- is missing.
Why does auto-dereferencing not happen? — Auto-deref only happens when it is a receiver i.e. the "self" in a method call foo.bar(). x is not a "self" argument and + is not a method call. So there's no auto-deref here. See What are Rust's exact auto-dereferencing rules? for detail.
I have
fn plus_one(x: &i32) -> i32 {
x + 1
}
fn plus_one_star(x: &i32) -> i32 {
*x + 1
}
fn plus_one_mut(x: &mut i32) -> i32 {
x + 1
}
fn plus_one_mut_star(x: &mut i32) -> i32 {
*x + 1
}
fn main() {
let a: i32 = 5;
let mut b: i32 = 5;
println!("{:?}", plus_one(&a));
println!("{:?}", plus_one_star(&a));
println!("{:?}", plus_one_mut(&mut b));
println!("{:?}", plus_one_mut_star(&mut b));
// I expect all to print '6' as I never actually mutate b
}
The third function, plus_one_mut, fails to compile with: error[E0369]: binary operation `+` cannot be applied to type '&mut i32'
Why does this function with the mutable reference fail to compile?
As the error message says:
binary operation + cannot be applied to type '&mut i32'
That's because it's not implemented. Reviewing the documentation for i32, you will see these implementations of Add:
impl Add<i32> for i32
impl<'a> Add<i32> for &'a i32
impl<'a> Add<&'a i32> for i32
impl<'a, 'b> Add<&'a i32> for &'b i32
You need to dereference &mut i32 to get to i32, which does have an Add implementation.
Why does it not have that implementation? I'm not sure. Perhaps you can submit a PR to Rust to add it... Personally, I've can't recall ever needing it. Usually if you have a &mut T it's because you want to update it, so you would have something like *foo += 1.
Say I have the following,
use std::io;
use std::io::Read;
#[derive(Debug)]
enum FooReadError {
UnexpectedEof,
IoError(io::Error),
}
impl From<io::Error> for FooReadError {
fn from(err: io::Error) -> FooReadError {
FooReadError::IoError(err)
}
}
fn read_n_bytes_to_vector<R: Read>(reader: &mut R, length: usize)
-> Result<Vec<u8>, FooReadError> {
let mut bytes = Vec::<u8>::with_capacity(length);
unsafe { bytes.set_len(length); }
let bytes_read = try!(reader.read(&mut bytes[..]));
if bytes_read != length {
Err(FooReadError::UnexpectedEof)
} else {
Ok(bytes)
}
}
fn do_some_read(reader: &mut Read) -> Vec<u8> {
read_n_bytes_to_vector(reader, 16).unwrap()
}
fn main() {
let v = vec![0, 1, 2, 3, 4, 5];
let mut cur = io::Cursor::<Vec<u8>>::new(v);
do_some_read(&mut cur);
}
The read_n_bytes_to_vector is supposed to take anything implementing the trait io::Read, read length bytes from it, and put them into a vector and return the vector.
The function do_some_read has a io::Read trait object. So, why then:
% rustc ./vec_read.rs
./vec_read.rs:29:5: 29:27 error: the trait `core::marker::Sized` is not implemented for the type `std::io::Read` [E0277]
./vec_read.rs:29 read_n_bytes_to_vector(reader, 16).unwrap()
^~~~~~~~~~~~~~~~~~~~~~
./vec_read.rs:29:5: 29:27 note: `std::io::Read` does not have a constant size known at compile-time
./vec_read.rs:29 read_n_bytes_to_vector(reader, 16).unwrap()
^~~~~~~~~~~~~~~~~~~~~~
I agree with the compiler that io::Read can't possibly implement Sized; but I'm passing a trait object — those are constant-size, so it should be okay here; **so why the error?* But wait, why does it even matter? The function isn't taking an io::Read for an arg (right?), it's taking a trait object too, because the arg is generic, and should take the full type of what's passed in.
Generics include the Sized bound by default; if you do not wish it to be required, you must add the ?Sized bound.
A trait object is not of constant size; u16 as Trait is two bytes, u32 as Trait is four bytes, &c.; only things like boxed trait objects (Box<Trait>) and trait object references (&Trait, &mut Trait) have a constant size, known at compile time (two words for the cited examples).
Because you only use an R by mutable reference, you can add the ?Sized bound successfully:
fn read_n_bytes_to_vector<R: ?Sized + Read>(reader: &mut R, length: usize)
-> Result<Vec<u8>, FooReadError> {
I am trying to understand Rust's reference.
fn main() {
let x: i32 = 0;
println!("{}", x+1); // this works
println!("{}", (&x)+1); // this works
//println!("{}", (&(&x))+1); // but this failed
}
What I get:
1
1
What does & actually do? Why can &x be added like it is an integer but not &(&x)?
& takes the reference of the operand. This can be thought of as finding the memory address that the value is stored at.
Your example works because + is implemented using the Add trait, which has the following variants:
impl Add<i32> for i32
impl<'a> Add<i32> for &'a i32
impl<'a> Add<&'a i32> for i32
impl<'a, 'b> Add<&'a i32> for &'b i32
That is, you can add any pair of references and non-references. However, your second example would have two levels of indirection (&&i32), and the Add trait isn't implemented for that many levels of reference.
In Rust, when we want a struct to contain references, we typically define their lifetimes as such:
struct Foo<'a> {
x: &'a i32,
y: &'a i32,
}
But it's also possible to define multiple lifetimes for different references in the same struct:
struct Foo<'a, 'b> {
x: &'a i32,
y: &'b i32,
}
When is it ever useful to do this? Can someone provide some example code that doesn't compile when both lifetimes are 'a but does compile when the lifetimes are 'a and 'b (or vice versa)?
After staying up way too late, I was able to come up with an example case where the lifetimes matter. Here is the code:
static ZERO: i32 = 0;
struct Foo<'a, 'b> {
x: &'a i32,
y: &'b i32,
}
fn get_x_or_zero_ref<'a, 'b>(x: &'a i32, y: &'b i32) -> &'a i32 {
if *x > *y {
return x
} else {
return &ZERO
}
}
fn main() {
let x = 1;
let v;
{
let y = 2;
let f = Foo { x: &x, y: &y };
v = get_x_or_zero_ref(&f.x, &f.y);
}
println!("{}", *v);
}
If you were to change the definition of Foo to this:
struct Foo<'a> {
x: &'a i32,
y: &'a i32,
}
Then the code won't compile.
Basically, if you want to use the fields of the struct on any function that requires it's parameters to have different lifetimes, then the fields of the struct must have different lifetimes as well.
I want to re-answer my question here since it's still showing up high in search results and I feel I can explain better. Consider this code:
Rust Playground
struct Foo<'a> {
x: &'a i32,
y: &'a i32,
}
fn main() {
let x = 1;
let v;
{
let y = 2;
let f = Foo { x: &x, y: &y };
v = f.x;
}
println!("{}", *v);
}
And the error:
error[E0597]: `y` does not live long enough
--> src/main.rs:11:33
|
11 | let f = Foo { x: &x, y: &y };
| ^^ borrowed value does not live long enough
12 | v = f.x;
13 | }
| - `y` dropped here while still borrowed
14 | println!("{}", *v);
| -- borrow later used here
What's going on here?
The lifetime of f.x has the requirement of being at least large enough to encompass the scope of x up until the println! statement (since it's initialized with &x and then assigned to v).
The definition of Foo specifies that both f.x and f.y use the same generic lifetime 'a, so the lifetime of f.y must be at least as large as f.x.
But, that can't work, because we assign &y to f.y, and y goes out of scope before the println!. Error!
The solution here is to allow Foo to use separate lifetimes for f.x and f.y, which we do using multiple generic lifetime parameters:
Rust Playground
struct Foo<'a, 'b> {
x: &'a i32,
y: &'b i32,
}
Now the lifetimes of f.x and f.y aren't tied together. The compiler will still use a lifetime that's valid until the println! statement for f.x. But there's no longer a requirement that f.y uses the same lifetime, so the compiler is free to choose a smaller lifetime for f.y, such as one that is valid only for the scope of y.
Here is another simple example where the struct definition has to use two lifetimes in order to operate as expected. It does not split the aggregate into fields of different lifetimes, but nests the struct with another struct.
struct X<'a>(&'a i32);
struct Y<'a, 'b>(&'a X<'b>);
fn main() {
let z = 100;
//taking the inner field out of a temporary
let z1 = ((Y(&X(&z))).0).0;
assert!(*z1 == z);
}
The struct Y has two lifetime parameters, one for its contained field &X, and one for X's contained field &z.
In the operation ((Y(&X(&z))).0).0, X(&z) is created as a temporary and is borrowed. Its lifetime is only in the scope of this operation, expiring at the statement end. But since X(&z)'s lifetime is different from the its contained field &z, the operation is fine to return &z, whose value can be accessed later in the function.
If using single lifetime for Y struct. This operation won't work, because the lifetime of &z is the same as its containing struct X(&z), expiring at the statement end; therefore the returned &z is no longer valid to be accessed afterwards.
See code in the playground.