From &mut Struct to Struct

Hello

This is my code:

struct Person {
    name: String,
    age: u8,
}

fn main() {
    let mut p = Person {
        name: "Foo Bar".into(),
        age: 20,
    };
    
    increment_age(&mut p);
}

// Person needs to be mutable here.
fn increment_age(p: &mut Person) {
    p.age = p.age + 1;
    
    // The only way I know to go from &mut Person to Person is by making new instance.
    // Is there a better way to do it?
    // Maybe cloning or something?
    let p2 = Person {
        name: p.name.to_string(),
        age: p.age,
    };
    
    do_something_immutable(p2);
}

// P must remain immutable here.
fn do_something_immutable(p: Person) {
    println!("{} is {} years old", p.name, p.age);
}

I have a struct Person and two functions. The first function increment_age needs to have Person as mutable because it changes the property of Person. increment_age calls the second function do_something_immutable. The second function does not need to have Person mutable.

How do I convert &mut Person to just Person in increment_age another way then creating a new object and copying the properties?

Your do_something_immutable seems to have the wrong signature.

As it stands now,

fn do_something_immutable(p: Person) { … }

means it needs owned access to an instance of type Person.

Owned access is even more restrictive than mutable access[1], and hence way on the opposite spectrum to immutable access. In fact, do_something_immutable can mutate its Person if it wants to, with this signature: Either through marking the argument as mutable (this only matters inside of the function, and it’s an implementation detail, not part of the signature)

fn do_something_immutable(mut p: Person) {
    // can mutate `p` here
}

or by assigning (i.e. moving) into a mutable local variable

fn do_something_immutable(mut p: Person) {
    let mut local_p = p;
    // can mutate `local_p` here
}

Either way… this signature also means that the do_something_immutable function will keep ownership of the Person it received indefinitely, and it will drop it (or perhaps let it go into some other global state, like into a global variable, or sent to another thread, or a new thread, or it could leak it, …).

So, as far as I can tell, the “correct” signature for do_something_immutable should be

fn do_something_immutable(p: &Person) {
    println!("{} is {} years old", p.name, p.age);
}

and that one should be straightforward to call inside of increment_age, feel free to give it a try :wink:


  1. there are ways to get ownership of a value through &mut … references; for example, you can do it as long as you can provide a replacement value; and also fn(&mut T) is actually quite similar to fn(T) -> T, as long as you somehow handle the panicking case ↩︎

4 Likes

Yes, Clone, please do read the book and also see the std lib.

1 Like

I tried cloning and it didn't work:

  Compiling playground v0.0.1 (/playground)
error[E0599]: no method named `clone` found for mutable reference `&mut Person` in the current scope
  --> src/main.rs:27:30
   |
27 |     do_something_immutable(p.clone());
   |                              ^^^^^ method not found in `&mut Person`
   |

Yeah I tried this signature: do_something_immutable(p: &Person). But the problem is that in my actual code I have a defined type:

type Route = (&'static str, &'static str, fn(&Routes, Http) -> BoxFuture<Result<HttpResponse, String>>);

fn(&Routes, Http) refers to fn do_something_immutable(p: Person). If I use p: &Person I also have to update my type to:

type Route = (&'static str, &'static str, fn(&Routes, &Http) -> BoxFuture<Result<HttpResponse, String>>);

Which resulted in a big mess of lifetime warnings etc.

The std lib link I posted shows how to derive clone with #[derive(Clone)].

1 Like

Ooohhw of course... Forgot that small but important piece. Thanks, now it works.

Instead of changing the Route type, can you copy or clone the Http value when passing it to the fn in the Route type?

EDIT: Copying or cloning may or may not be the right thing here. Does your Http type have mutable state?

Oh, it's this from your other post:

struct Http {
    method: String,
    path: String,
    host: String,
    cookies: std::collections::HashMap<String, String>,
}

If this is all immutable (it looks like it is) then cloning is probably the right thing, since this is an experimental learning project and performance is not critical. Or, if you want to learn more about lifetimes and ownership you could change the Route type and work on the problems that the compiler reported.

1 Like

I can't quite imagine the scenario from this description alone. Feel free to share some code, e. g. the attempt with the "big mess of lifetime warnings (errors presumably)".

Actually, on second thought, I can imagine a problem you could be having: maybe it's because of the future type.. maybe you'll just need to write the explicit higher-ranked lifetimes once you have more than one reference-typed argument

type Route = (&'static str, &'static str, for<'a> fn(&'a Routes, &'a Http) -> BoxFuture<'a, Result<HttpResponse, String>>);
2 Likes

You can view my full code here:

Looks like Http is the request, so it would have a much shorter lifetime than Routes.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.