How to properly annotate lifetimes - rust

I've been struggling with trying to understand how to properly annotate lifetimes in my application. I've simplified the actual code I had to this:
struct Item<'a> {
rf: &'a i32,
}
impl<'a> Item<'a> {
fn new(main: &'a App) -> Item<'a> {
Item{
rf: &main.i
}
}
}
struct App<'a> {
i: i32,
vec: Vec<Item<'a>>
}
impl<'a> App<'a> {
fn new() -> App<'a> {
App {
i: 32,
vec: vec![]
}
}
fn init(&mut self) {
self.vec.push(Item::new(self))
}
fn update(&self) {
for item in self.vec.iter() {
println!("{}", item.rf)
}
}
}
fn main() {
let app = App::new();
app.init();
app.update();
}
So there's a vector of items that hold a reference to something in App, which I know would exist as long as the app is alive, but the borrow-checker doesn't accept this. Could someone explain what's wrong with this code and how I could fix it?
You can find this code in rust playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ee8980c9e1b2a0622548525dbcf9f50f

I think the problem lays with how rust handles references and infers them. Here's a possible solution.
In short, we tell the rust compiler that in the App struct the i member lives for 'a length. This member we can then share with another struct that lives for at least 'a. we achieve this by telling the Item struct when creating it, that it at least has to live for at least the lifetime of the App struct. Because the compiler can be a bit picky about inferring and anonymous lifetimes we have to be explicit. We do this by adding a 'b to the new method of the Item struct. When we then call this method with the lifetime 'a of the app struct, the compiler knows that Item lives for as long as App. Sorry for the short explanation cause more than I probably know goes on here.
struct Item<'a> {
rf: &'a i32,
}
impl<'a> Item<'a> {
fn new<'b>(main: &'b App<'a>) -> Item<'a> {
Item{
rf: main.i
}
}
}
struct App<'a> {
i: &'a i32,
vec: Vec<Item<'a>>
}
impl<'a> App<'a> {
fn new() -> App<'a> {
App {
i: &32,
vec: vec![]
}
}
fn init(&mut self) {
let item = Item::new(self);
self.vec.push(item)
}
fn update(&self) {
for item in self.vec.iter() {
println!("{}", item.rf)
}
}
}
fn main() {
let mut app = App::new();
app.init();
app.update();
}

Related

Immutable struct with mutable reference members

Is my understanding correct that in Rust it is not possible to protect reference members of a struct from modification while having the reference target values mutable? (Without runtime borrow checking that is.) For example:
struct MyData<'a> {
pub some_ref: &'a mut i32,
}
fn doit<'a>(data: &mut MyData<'a>, other_ref: &'a mut i32) {
// I want to be able to do the following here:
*data.some_ref = 22;
// but make it impossible to do the following:
data.some_ref = other_ref;
}
Not being able to change the reference value may be useful in certain FFI situations. FFI and the performance requirements reasons prevent the use of runtime borrow checking here.
In C++ it can be expressed like this:
struct MyData {
int* const some_ref;
};
void doit(const MyData &data, int* other_ref) {
// this is allowed:
*data.some_ref = 22;
// this is not:
data.some_ref = other_ref; // compile error
}
You can create a wrapper type around the reference. If the constructor is private, and so is the wrapped reference field, you cannot replace the reference itself. You can then implement DerefMut to allow changing the referent.
pub struct ImmRef<'a> {
inner: &'a mut i32,
}
impl<'a> ImmRef<'a> {
fn new(inner: &'a mut i32) -> Self { Self { inner } }
}
impl std::ops::Deref for ImmRef<'_> {
type Target = i32;
fn deref(&self) -> &Self::Target { &*self.inner }
}
impl std::ops::DerefMut for ImmRef<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.inner }
}
struct MyData<'a> {
pub some_ref: ImmRef<'a>,
}
fn doit<'a>(data: &mut MyData<'a>, other_ref: &'a mut i32) {
// I want to be able to do the following here:
*data.some_ref = 22;
// but make it impossible to do the following:
// data.some_ref = other_ref;
}
You can mark the newtype #[repr(transparent)] for FFI purposes.
But do note that if the code has some ImmRef<'a> available it can use tools such as std::mem::replace() to replace the reference.
Rust does not allow you to specify the mutability of individual fields like you can via const in C++. Instead, you should simply encapsulate the data by making it private and only allow modification through methods that you dictate:
struct MyData<'a> {
some_ref: &'a mut i32,
}
impl MyData<'_> {
pub fn set_ref(&mut self, other: i32) {
*self.some_ref = other;
}
}
That way, the field some_ref cannot be modified directly (outside of the module) and must use the available method.

Shared ownership inside a struct (mutable_borrow_reservation_conflict warning)

The following code compiles and runs but emits a mutable_borrow_reservation_conflict warning.
My goal is to have a field all_ops owning a set of Op's implementations (readonly) where each op can be referenced in another container in the same struct (and when the main all_ops container is cleared, used_ops access becomes illegal as expected)
Of course, one could use Rc but it causes performance issues.
Do you have an idea to do that properly ? (i.e. a way which will not become an hard error in the (near?) future).
trait Op {
fn f(&self);
}
struct OpA;
impl Op for OpA {
fn f(&self) {
println!("OpA");
}
}
struct OpB;
impl Op for OpB {
fn f(&self) {
println!("OpB");
}
}
struct Container<'a> {
all_ops: Vec<Box<dyn Op>>,
used_ops: Vec<&'a Box<dyn Op>>, // data pointing to data in all_ops field
}
fn main() {
let v: Vec<Box<dyn Op>> = vec![Box::new(OpA), Box::new(OpB)];
let mut c = Container { all_ops: v, used_ops: Vec::new() };
c.used_ops.push(&c.all_ops.get(0).unwrap());
c.used_ops.push(&c.all_ops.get(1).unwrap());
c.used_ops.push(&c.all_ops.get(0).unwrap());
for op in c.used_ops {
op.f();
}
c.all_ops.clear();
// c.used.first().unwrap().f(); // cannot borrow `c.all` as mutable because it is also borrowed as immutable
}
Rust playground
If I replace used_ops: Vec<&'a Box<dyn Op>>
by used_ops: Vec<&'a dyn Op>, it seems sufficient to fix the warning.
Unfortunately, Container isn't movable even if all references in used_ops are on objects allocated in the heap (and I understand why since there are references to (inner parts of) the object).
trait Op {
fn f(&self);
}
struct OpA;
impl Op for OpA {
fn f(&self) {
println!("OpA");
}
}
struct OpB;
impl Op for OpB {
fn f(&self) {
println!("OpB");
}
}
struct Container<'a> {
all_ops: Vec<Box<dyn Op>>,
used_ops: Vec<&'a dyn Op>, // data pointing to data in all_ops field
}
fn main() {
let v: Vec<Box<dyn Op>> = vec![Box::new(OpA), Box::new(OpB)];
let mut c = Container { all_ops: v, used_ops: Vec::new() };
c.used_ops.push(c.all_ops.get(0).unwrap().as_ref());
c.used_ops.push(c.all_ops.get(1).unwrap().as_ref());
c.used_ops.push(c.all_ops.get(0).unwrap().as_ref());
for op in c.used_ops.iter() {
op.f();
}
// let c2 = c; // cannot move out of `c` because it is borrowed
}
Playground

Add an element to a mutable vector list in Rust

Here is a link to a playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1e82dcd3d4b7d8af89c5c00597d2d938
I am a newbie learning rust and trying to simply update a mutable vector on a struct.
struct Friend<'a> {
name: &'a str
}
impl <'a> Friend<'a> {
fn new(name: &'a str) -> Self { Self { name } }
}
struct FriendsList<'a> {
name: &'a str,
friends: Vec<Friend<'a>>
}
impl <'a> FriendsList<'a> {
fn new(name: &'a str, friends: Vec<Friend<'a>>) -> Self { Self { name, friends } }
fn add_new_friend(&self, friend: Friend) {
// how to make this work?
todo!()
// self.friends.push(friend)
}
}
fn main() {
let friends_list = FriendsList::new("George",
vec![
Friend::new("bob"),
Friend::new("bobby"),
Friend::new("bobbo")
]
);
}
specifically how do I make this fn add_new_friend(&self, friend: Friend) method work? That is, push a new element to field friends on the FriendsList struct. Is there a more idiomatic approach? When I try making things mutable, I get a whole bunch of errors I am not sure how to fix...
You have to borrow self mutably:
impl <'a> FriendsList<'a> {
// [...]
fn add_new_friend(&mut self, friend: Friend<'a>) {
self.friends.push(friend)
}
}

Is it possible to move even with immutable borrows?

use std::marker;
use std::ops;
pub struct Shared<'r, T: 'r> {
data: *mut T,
_pd: marker::PhantomData<&'r T>,
}
impl<'r, T> Shared<'r, T> {
pub fn new(value: T) -> Shared<'r, T> {
let boxed = Box::new(value);
Shared {
data: Box::into_raw(boxed),
_pd: marker::PhantomData,
}
}
pub fn as_ref(&self) -> SharedRef<'r, T> {
SharedRef {
data: self.data,
_pd: marker::PhantomData,
}
}
}
impl<'r, T> ops::Deref for Shared<'r, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.data }
}
}
pub struct SharedRef<'r, T: 'r> {
data: *mut T,
_pd: marker::PhantomData<&'r T>,
}
impl<'r, T> ops::Deref for SharedRef<'r, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.data }
}
}
impl<'r, T> Drop for Shared<'r, T> {
fn drop(&mut self) {
unsafe {
Box::from_raw(self.data);
}
}
}
fn main() {
let s = Shared::new(42);
let s_ref = s.as_ref();
{
let s1 = s;
}
// lifetime should end here
println!("{}", *s_ref);
}
What I wanted to express was a mix between a Box and an Arc. A uniquely owned pointer that is also capable of giving out references.
The problem is that I want to be able to move Shared around even if there are currently immutable borrows to it. It should be legal in this scenario because it is heap allocated.
The problem is that I have no idea how to express this.
fn main() {
let s = Shared::new(42);
let s_ref = s.as_ref();
{
let s1 = s;
}
// lifetime should end here
println!("{}", *s_ref);
}
Here I move s into a scope with "less" lifetime than it had before. But now after I have moved s into s1, s_ref should not be accessible anymore. So what I want to say is that it is okay to move a Shared if the lifetime does not get smaller.
Can this be expressed in Rust?
The reason Rust allows you to move out of the Shared is that you haven't tied the lifetime of the returned SharedRef to it:
pub fn as_ref(&self) -> SharedRef<'r, T> {
SharedRef {
data: self.data,
_pd: marker::PhantomData,
}
}
Annotating the &self fixes that:
pub fn as_ref(&'r self) -> SharedRef<'r, T> { .. }
My current understanding is that the key difference here is that this says that the lifetime of the SharedRef now matches the lifetime of the borrow of self, keeping the borrow alive. Indeed it doesn't have to be the same lifetime ('r) as in the Shared; it works with a new lifetime just for the borrow/return:
pub fn as_ref<'b>(&'b self) -> SharedRef<'b, T> { .. }
This also disallows the move.
As for the bonus part of the question, where you want to allow moving as long as it's to something with a long enough lifetime, I think the answer is no. The only way I know to stop something being moved at all is to borrow it, and that stops any move.

Construct a Vector of any Type with a Trait Constraint

I'd like to have a struct, with a member that is a Vec with a Trait Constraint on the types in the Vector. Right now, this is what I am trying:
pub trait SomeTrait {
fn some_function(&self);
}
pub struct SomeStruct<T: SomeTrait> {
member: Vec<T>
}
impl<T: SomeTrait> SomeStruct<T> {
pub fn new() -> SomeStruct<T> {
SomeStruct {
member: Vec::new()
}
}
}
fn main() {
let mut some_struct = SomeStruct::new();
}
And the compiler is giving me:
error: the trait 'sometrait::SomeTrait' is not implemented for the type '_'
let mut some_struct = SomeStruct::new();
^~~~~~~~~~~
note: required by 'somestruct::SomeStruct<T>::new'
let mut some_struct = SomeStruct::new();
^~~~~~~~~~~
I've tried placing <T: SomeTrait> in various places throughout, but just getting even stranger compiler errors, so those attempts must be way off base.
Thanks in advance for any help!
The problem is that you haven't given the compiler enough information to figure out what T is. That's what the _ is for: it's having to infer the parameter to SomeStruct.
Also, there are no types anywhere in this example that implement SomeTrait. If you fix both of these problems, it works (with some warnings):
pub trait SomeTrait {
fn some_function(&self);
}
pub struct SomeStruct<T: SomeTrait> {
member: Vec<T>
}
impl<T: SomeTrait> SomeStruct<T> {
pub fn new() -> SomeStruct<T> {
SomeStruct {
member: Vec::new()
}
}
}
impl SomeTrait for i32 {
fn some_function(&self) {}
}
fn main() {
let mut some_struct_a: SomeStruct<i32> = SomeStruct::new();
let mut some_struct_b = SomeStruct::<i32>::new();
}

Resources