Reputation: 5245
Why, in the code below, does using an array slice work, but using a slice of a Vec
doesn't?
use rand::{rngs::adapter::ReadRng, RngCore};
use std::io::Read;
struct MyRng {
rng: Box<dyn RngCore>,
}
pub fn main() {
// Version 1: error
//
let data = Vec::<u8>::new();
let data_slice = data.as_slice();
// Version 2: works
//
// let data_slice = &[0_u8][..];
// Version 3: error (!?!)
//
// let data = [0_u8];
// let data_slice = &data[..];
let read = Box::new(data_slice) as Box<dyn Read>;
let rng = Box::new(ReadRng::new(read));
// With this commented out, all versions work.
MyRng { rng };
}
There are a few things that puzzle me:
data
is dropped while borrowed, but points to the end of the scope - isn't everything dropped by then?MyRng
instantiation, everything works fine?Upvotes: 2
Views: 90
Reputation: 15115
The Rust Reference on Lifetime Elision:
If the trait has no lifetime bounds, then the lifetime is inferred in expressions and is
'static
outside of expressions.
So by default boxed trait objects get a 'static
bound. So this struct:
struct MyRng {
rng: Box<dyn RngCore>,
}
Actually expands to this:
struct MyRng {
rng: Box<dyn RngCore + 'static>,
}
Which forces you to produce either an owned type or a 'static
reference in order to satisfy the bound. However, you can opt out of the implicit 'static
bound entirely by making your struct generic, after which all the different versions of the code compile:
use rand::{rngs::adapter::ReadRng, RngCore};
use std::io::Read;
struct MyRng<'a> {
rng: Box<dyn RngCore + 'a>,
}
pub fn main() {
// Version 1: now works
let data = Vec::<u8>::new();
let data_slice = data.as_slice();
let read = Box::new(data_slice) as Box<dyn Read>;
let rng = Box::new(ReadRng::new(read));
let my_rng = MyRng { rng };
// Version 2: still works
let data_slice = &[0_u8][..];
let read = Box::new(data_slice) as Box<dyn Read>;
let rng = Box::new(ReadRng::new(read));
let my_rng = MyRng { rng };
// Version 3: now works
let data = [0_u8];
let data_slice = &data[..];
let read = Box::new(data_slice) as Box<dyn Read>;
let rng = Box::new(ReadRng::new(read));
let my_rng = MyRng { rng };
}
To answer your individual questions more directly:
What's the difference between the three approaches, if all are in the same scope?
Scope and lifetimes aren't the same thing, but the main difference between the 3 approaches is that approach #2 creates a static slice. When you hardcode some &T
reference into your code without it referring to any data owned by any variable then it gets written into the read-only segment of the binary and gets a 'static
lifetime.
The error says that data is dropped while borrowed, but points to the end of the scope - isn't everything dropped by then?
Yes, but your type, by definition, requires the passed value to be bounded by a 'static
lifetime, and since approaches #1 and #3 do not produce such values the compiler rejects the code.
Why if I remove the
MyRng
instantiation, everything works fine?
Because there's nothing wrong with the definition of your MyRng
struct, only when you try to instantiate it incorrectly does the compiler complain.
Upvotes: 5
Reputation: 35512
[0_u8]
is a constant, so it undergoes rvalue static promotion, allowing references to it to have a static lifetime, so it won't be dropped. Your code fails because you have a slice that borrows the local variable data
yet tries to create a MyRng
variable from it.
Upvotes: 1