How do you work around something like this?
struct Foo;
impl Foo {
fn bar(self: &mut Self) {
self.baz();
}
fn baz (self: &mut Self) {
}
}
fn main() {
let foo = Foo{};
foo.bar();
}
How do you work around something like this?
struct Foo;
impl Foo {
fn bar(self: &mut Self) {
self.baz();
}
fn baz (self: &mut Self) {
}
}
fn main() {
let foo = Foo{};
foo.bar();
}
As the compiler says, you didn't declare foo as mutable.
let mut foo = Foo{};
Followup question... the following fails with
cannot borrow `*self` as mutable because it is also borrowed as immutable
struct Foo<'a> (&'a str);
impl<'a> Foo<'a> {
fn init(self: &'a mut Self) {
let value = self.get_value();
self.do_something(value);
}
fn get_value(self: &'a Self) -> &'a str {
self.0
}
fn do_something(self:&'a mut Self, _: &'a str) {
}
}
fn main() {
let mut foo = Foo ("hello world!");
foo.init();
}
And if I change get_value
to accept self: &'a mut Self
then I get:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
Ideas? Gotta run - but will be back later and excited to see some thoughts on this
Well, the problem here, is that init
will first immutably bind &mut self
until value
is dropped, meaning that you can't run a &mut self
function, such as do_something
. It can't guarantee you won't modify self
in such a way that would invalidate the borrowed reference, &'a str
.
Don't mark &Self
or &mut Self
with lifetimes, because they don't represent the lifetimes of the strings inside. But even if you do that it won't compile because of what @OptimisticPeach said.
It should compile.
Is that a general rule of thumb, or just specific here? I do see that removing that here fixes it...
Where did you found the code like (self: &mut Self)
? I know it's valid syntax, but your snippet is the first code I've ever seen to use it. Afaik, everyone just write (&self)
and (&mut self)
instead.
In this case.
This is the thought process.
struct Foo<'a> (&'a str);
impl<'a> Foo<'a> {
fn new(string: &'a str) -> Self {
Self(string)
}
fn set(&mut self, string: &'a str) {
self.0 = string;
}
fn get(&self) -> &'a str {
self.0
}
}
let's examine this code to show how it works. Foo
stores a reference to a string slice of some lifetime. Foo::new
creates a new foo with the same lifetime of the parameter. Note: it is true that Foo<'a>: 'a
but it is not true that &'b Foo<'a>: 'a
because 'a
could outlive 'b
. (Note: that if 'a == 'b
then &'b Foo<'a>: 'a
is true because it boils down to 'b: 'a
, which is trivially true if 'b == 'a
)
let foo = Foo::new("hi"); // Foo<'static>
let foo_ref = &foo_ref; // ref is not static, so its lifetime is outlived by 'static
Because of this, we can say that 'a
does not depend on &self
. Instead it depends on some outside source. So we shouldn't mark references to self
with lifetimes.
In general, don't mark lifetimes unless the compiler complains about it. Then only mark as few lifetimes as possible.
Another way to figure out which lifetimes are needed is to track how you are getting the lifetime. In my example, we get 'a
from &'a str
, which we get from an outside source in Foo::new
. This means that 'a
will necessarily outlive Foo<'a>
because 'a
depends on some outside source, and that it shouldn't be related to self
in any other way.
The reason that 'a
will necessarily outlive Foo<'a>
is because the outside source is now borrowed, and so can't move or be dropped. So it must live for as long as 'a
which will live until Foo<'a>
is dropped.
It is possible to get lifetimes that refer to the struct in safe Rust. But this only really comes up in self-referential types. These types are hard to work with correctly, because of issues like yours but where you can't remove the lifetimes. So instead of dealing with the lifetimes you would use the Pin
api along with some unsafe
. In fact, this is exactly how async
/await
works!
safe self-referential type example
edit for clarity and correctness based off of @TomP and @Yandros 's comments later on
&self
is sugar for self: &Self
, same with &mut self
being sugar for &mut Self
. &Self
can be generalized to other types like so self: Box<Self>
, self: Rc<Self>
, self: Arc<Self>
under the arbitrary self type RFC. So far only &_
, &mut _
, Box<_>
, Rc<_>
, Arc<_>
are supported, but if it lands, many other types could also do this and use the method call sugar.
Fantastic explanation... makes my brain hurt but I kindof understand it... don't think I'd be able to see it for myself when writing code yet, but at least it makes sense. Thanks!
Self referential types are extremely niche so I wouldn't be surprised if they never come up. But lifetimes are worth investing your time to understand. Though that may take a while because lifetimes are hard.
Yeah I know that, but that feature is mainly for other types like Box, not for plain references. Imo self: &Self
should not be promoted for the sake of consistency, and afaik, most Rust codes available on the web does not use such pattern. That's why I wonder where OP learned this pattern.
Ok, I wasn't aware that you knew that. I agee that we should use &self
instead of self: &Self
Do you mind elaborating on this a bit more? Specifically:
How do you read Foo<'a>: 'a
in English? Looks like the same syntax as Trait Inheritance...
So I see that 'a
comes from the input, but what is 'b
?
I'm also not clear on what &'b Foo<'a>: 'a
means exactly, but I guess that will flow from the above question too...
Ok
Type: lifetime
means that Type
outlives lifetime
lifetime: lifetime
means that the first lifetime outlives the second lifetime'b
is the lifetime of the reference, which may or may not be the same as 'a
&'b Foo<'a>: 'a
means: a shared reference to Foo<'a>
with the lifetime 'b
outlives lifetime 'a
[this is only the case of 'b == 'a
]'a
[strictly] outlives 'b
, because 'a
will refer to a larger scope then 'b
(note that because of nll, the scopes that I am talking about are not lexical scopes, but are rather invisible and refer to how long certain references live)for<'b> &'b Foo<'a>: 'a
but I didn't want to get into universal quantification syntax because it is strange and I didn't want to overload information too muchedits for clarity based off of @TomP and @Yandros's comments later on
Thanks again!!
I looked for some of this in the Rust book and now I see - it's in the Advanced Lifetimes section under Lifetime Subtyping
:
we can declare a lifetime
'a
as usual and declare a lifetime'b
that lives at least as long as'a
by declaring'b
using the syntax'b: 'a
.
Great, will need to go back and re-read some of what you wrote above, armed with this knowledge... though I like the way you put it a bit better (not that 'b lives at least as long as 'a, but that 'b must outlive 'a... more active language, easier to grok)
The forall 'b
addition actually helps. Not sure why, but it does something to sink it in a bit.
OK so let's see if I got this right (now that I've written it I see I'm just repeating what you said it seems... but helps to double-check): - it's basically boiling down to saying that a reference to a thing cannot live longer than the thing itself... and in this specific case - since Foo
is created from 'a
, it's tied to 'a
, so a reference to Foo must not live longer than 'a
in order to be valid?
I guess next step would be to go and look at your self-referential example and try to understand why fn set_ptr(&'a self) {
can't be fn set_ptr(&self) {
... I get you're saying that this almost never comes up but at a glance it looks exactly like the kind of thing I'm trying to do for some reason, i.e. have a struct update itself but not thinking through all the implications...
Heading to bed now, hopefully the answer will appear in my dreams
This is correct!
Ok, if you can give an example that is closer to what your actual problem is, then I or someone else here on the forums can give more insight on other was on organizing your code differently to avoid self-referential types. Self-referential types are a pain to deal with so I would really try and avoid them if you can.
Unfortunately the first statement's "lives at least as long as" was correct – lifetime('b) ≥ lifetime('a)
– while the second's "outlive" means lifetime('b) > lifetime('a)
. Notice the difference: the first is a reflexive inequality while the second is not. When the compiler analyzes such requirements it frequently chooses 'b = 'a
, which the second formulation excludes.
This is a matter of definition, if you take outlives to mean >
then yes it is incorrect, but if you take outlives to mean >=
then using outlives is fine. Basically saying 'a
outlives 'a
isn't fundamentally wrong. It may seem strange, but that is fine because I find that using the word "outlives" usually makes relationships more clear than "lives at least as long as".
By that logic, the statement "He xyz
more than anyone" is acceptable, for arbitrary xyz
, even though the statement clearly excludes the subject and thus leaves a null set.