New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rust does not allow narrowing the mutable references #27031
Comments
Variant &muts are not sound: 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);
} |
@gankro You're right - I haven't thought about that. Correct me if I'm wrong, however, but this is only when the To explain why - I've tried to create a mutable zipper. Effectively it is a recursive structure where each element refers to one created in parent structure and they all go out of scope |
/cc @nikomatsakis @aturon , is @gankro correct that this is unsound? if so, it should be closed. |
Discussed this more on IRC. I had originally only skimmed the details and had snapped "&'a mut T is invariant in T" to "&mut is invariant in all args", but this isn't actually necessary. The Also this works: type Test<'a> = &'a mut Fn(&'static str) -> u32;
fn matched_lifetimes<'a>(_: &'a u8, _: Test<'a>) {}
fn main() {
let x: Test<'static> = panic!();
{
let y: u8 = 0;
let z = &y;
matched_lifetimes(z, x);
}
} which suggests we already support this in a limited way. So yes, I think this is a legit bug. |
I don't see the unsoundness here (in making |
For the record, you can ATM create a enum BL<'a> {
Root,
Link(&'a mut BLLike, &'a mut u32)
}
trait BLLike {
fn val(&mut self) -> BL;
}
impl<'a> BLLike for BL<'a> {
fn val(&mut self) -> BL {
match self {
&mut BL::Root => BL::Root,
&mut BL::Link(ref mut cdr, ref mut car) => BL::Link(*cdr, car)
}
}
}
fn traverse<'a>(mut u: u32, mut l: BL) {
if u > 0 {
traverse(u-1, BL::Link(&mut l, &mut u))
} else {
while let BL::Link(cdr, car) = l {
println!("u={:?}", car);
l = cdr.val();
}
}
}
fn main() {
traverse(14, BL::Root);
} |
I'm surprised by this, I expect |
Oh, wait. I see what it is. |
The situation is caused by the defaulting rules around trait object lifetimes. Specifically, the fullly expanded version of the struct in question is: struct Foo3<'a> {
a: &'a mut (Fn(&'static str) -> u32 + 'a)
} While it is true that Now, it is true that you can assign So I would say the current behavior is Working As Designed. |
To be clear, I'd love to overcome have the original code work, but it'd require a fairly subtle typing rule to make it sound. I guess we could add a rule for subtyping of |
@nikomatsakis Initally I didn't have traits at all - it was closer to: enum Tree<T> {
Bin(*mut Tree<T>, T, *mut Tree<T>),
Leaf
}
enum TreeZipper<'a, T : 'a> {
Top,
Left(&'a mut TreeZipper<'a, T>, T, Tree<T>),
Right(Tree<T>, T, &'a mut TreeZipper<'a, T>)
}
fn cast<'a : 'b, 'b, T : 'a>(foo: TreeZipper<'a, T>) -> TreeZipper<'b, T> {
foo
} @arielb1 This still does no compile: enum BL<'a> {
Root,
Link(&'a mut BLLike, &'a mut u32)
}
trait BLLike {
fn val(&mut self) -> BL;
}
fn cast<'a : 'b, 'b>(foo: BL<'a>) -> BL<'b> {foo} |
It is not a question of traits (in fact, my point was that traits are not special cased, which is why you get an error here). The point is that you have the reference to 'a inside the referent type in both cases (that is, the type that the &mut points at). That makes it "invariant", meaning it cannot be shortened (or else the language would be unsound). In the trait case however this is less obvious because it appears due to the implicit lifetime bound. Niko -------- Original message -------- From: Maciej Piechotka notifications@github.com Date:07/18/2015 01:58 (GMT-05:00) To: rust-lang/rust rust@noreply.github.com Cc: Niko Matsakis niko@alum.mit.edu Subject: Re: [rust] Rust does not allow narrowing the mutable references
(#27031) enum Tree { enum TreeZipper<'a, T : 'a> { fn cast<'a : 'b, 'b, T : 'a>(foo: TreeZipper<'a, T>) -> TreeZipper<'b, T> { enum BL<'a> { trait BLLike { fn cast<'a : 'b, 'b>(foo: BL<'a>) -> BL<'b> {foo} |
Anyway, you can bypass this by restructuring: struct Foo3<'a> {
a: &'a mut Fn(&'static str) -> u32 + 'a
}
fn foo3<'a : 'b, 'b>(foo: Foo3<'a>) -> Foo3<'b> {
match foo { Foo3 { a } => Foo3 { a: a }}
}
fn main() {} In the enum BL<'a> {
Root,
Link(&'a mut BLLike, &'a mut u32)
}
trait BLLike {
fn val(&mut self) -> BL;
}
fn cast<'a : 'b, 'b>(foo: BL<'a>) -> BL<'b> {
match foo { BL::Root => BL::Root, BL::Link(a,b) => BL::Link(a,b) }
} |
Anyway, this may be a reason to have |
So, years later: I think this ticket is basically not actionable, right? I would imagine something like
Would be RFC territory. Maybe not. I dunno. |
I think we should close this. It's a...quirk of the type system. =) |
Currently rust does allow to narrow the immutable references as long as the don't appear in contravariant position:
It seems that if I have a mutable reference for
'a
lifetime I should be able to turn it into mutable reference for'b
lifetime as long as the'b
is narrower then'a
however the following code is not allowed in Rust:The text was updated successfully, but these errors were encountered: