[These posts have been extracted from this Discord conversation, so as not to require readers to use their Discord account to read it 😔]
HI!
Question and idea about narrowing lifetime of mutable references.
In rust code when developing finite state machines, behaviour trees and other algorithms we often need to pass around Context on which these algorithms will work.
If Context only contains shared references to data, then everything can be coerced to one lifetime
struct Context<'a> {
ref1: &'a SomeData1<'a>,
ref2: &'a SomeData2<'a, 'a, 'a>,
}
But if we need mutable references and SomeData contains mutable references itself, then a lot of lifetimes are introduced:
struct Context<'a, 'w, 's> {
ref1: &'a mut SomeData1<'w>,
ref2: &'a mut SomeData2<'w, 's>,
}
And we can not shorten mutable reference lifetimes as it could be unsound.
Rust does not allow narrowing the mutable references
Real life example from bevy:
struct Context<'a, 'w, 's>{
query_1: &'a mut Query<'w, 's, ...>,
item: &'a mut QueryItem<'w>,
}
And it gets even worse if i need to pass &'c mut &'b mut Context<'a, 'w, 's>
(mutable reference of mutable reference if one algorithm uses another algorithm inside it and so on)
It would be nice if all lifetimes could be constrained to one shortest lifetime, but it is not sound due to this:
fn overwrite<T: Copy>(input: &mut T, new: &mut T) {
*input = *new;
}
fn main() {
let mut forever_str: &'static str = "hello";
{
let string = String::from("world");
overwrite(&mut forever_str, &mut &*string);
}
// Oops, printing free'd memory
println!("{}", forever_str);
}
So i had an idea: Add new lifetime modifier to rust to mark that only 'static
lifetime data can be assigned to data accessed through lifetime with this new modifier. Then this problem would be resolved.
For example, lets mark this new lifetime in code as ~a
.
Then code like this would be possible:
// Simplifying mutable references
fn new_lifetime<'a, 'b: 'a, T>(val: &'a mut &'b mut T) -> &~a mut T {
val
}
// Context example to modify static data
struct Foo<'a> {
buff: &'a mut String
}
struct Context<~f> { // We do not need to track two lifetimes
foo: &~f mut Foo<~f>
}
fn test<'f, 'a>(foo: &'f mut Foo<'a>) {
let ctx = Context { foo };
*ctx.foo.buff = String::new("New value!"); // Static data can be modified
}
// Bevy example where we need to track only shortest lifetime
struct Context<~a>{
query_1: &~a mut Query<~a, ~a, ...>,
item: &~a mut QueryItem<~a>,
}
Longer idea explanation: https://www.reddit.com/r/rust/comments/13wkx2b/idea_of_a_new_lifetime_modifier_to_avoid_lifetime/
Wanted to get expert opinion on this idea because something like this could simplify a lot of code in my case.
Would something like this be possible in rust type system in future, feasible? How do you handle such Context structures?