Multiple mutable methods

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{};
2 Likes

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 :slight_smile:

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.

2 Likes

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.

2 Likes

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

3 Likes

&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!

1 Like

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.

1 Like

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.

1 Like

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:

  1. How do you read Foo<'a>: 'a in English? Looks like the same syntax as Trait Inheritance…

  2. So I see that 'a comes from the input, but what is 'b?

  3. 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

  1. the syntax Type: lifetime means that Type outlives lifetime
    a. Similar to how lifetime: lifetime means that the first lifetime outlives the second lifetime
  2. 'b is the lifetime of the reference, which may or may not be the same as 'a
    a. I did this is because there is no guarantee that these lifetimes are correlated
  3. So all together &'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. Note that this is a false statement if '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)
    b. the actual statement should be 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 much

edits for clarity based off of @TomP and @Yandros’s comments later on

2 Likes

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 :zzz:

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.

1 Like

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.

3 Likes

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.