Include another struct without moving

So I have two structs. Bar takes a Foo as an attribute. But Bar also has an attribute which should equal an attribute of Foo.

What is the most elegant way to solve this:

struct Foo {
    a: u8,
}
struct Bar {
    foo: Foo,
    x: u8,
    y: u8,
}

fn main() {
    
    let mut my_foo = Foo {
        a: 6,
    };
    
    let mut my_bar = Bar {
        foo: my_foo,
        x: my_foo.a,
        y: 5,
    };
}              

Error:


error[E0382]: use of moved value: `my_foo`
  --> src/main.rs:18:12
   |
12 |     let mut my_foo = Foo {
   |         ---------- move occurs because `my_foo` has type `Foo`, which does not implement the `Copy` trait
...
17 |         foo: my_foo,
   |              ------ value moved here
18 |         x: my_foo.a,
   |            ^^^^^^^^ value used here after move

error: aborting due to previous error; 3 warnings emitted

One solution is to add let x = my_foo.a; but that does not look clean in my actual code.

The code runs sequentially from top to bottom. Easiest fix would be to use my_foo.a before moving out the my_foo.

let my_bar = Bar {
    x: my_foo.a,
    y: 5,
    foo: my_foo,
};
4 Likes

(but this will only work if the type of field a is Copy)

Okay but this is my actual code:

async fn order_form(mysql: web::Data<MySQL>, form: web::Form<OrderForm>, req: HttpRequest, mollie: web::Data<mollie::Mollie>) -> String {
    let mut client = Client {
        id: 0,
        firstname: form.firstname.to_string(),
        lastname: form.lastname.to_string(),
        email: form.from_email.to_string(),
        tel: form.tel.to_string(),
        saved_account: false,
    };


    let distributor = data::get_distributor_by_subdomain(&mysql, get_subdomain_part(req.headers().get("Host").unwrap().to_str().unwrap())).await.unwrap();
    let distributor_voucher = data::get_distributor_voucher(&mysql, form.voucher).await.unwrap();

    let client_id = data::add_client(&mysql, &client).await;
    client.id = client_id;

    let mut sale = Sale {
        id: 0,
        client: client,
        payment_id: "".to_string(),
        paid: false,
    };

    let balance = distributor_voucher.amount;

    let sale_id = data::add_sale(&mysql, &sale).await;
    sale.id = sale_id;

    let mut payment_id = "".to_string(); // payment_id is passed by reference and gets a new value
    let payment_url = mollie.make_payment(&data::get_sale(&mysql, data::ByIdOrPaymentId::ById(sale_id)).await.unwrap(), &mut payment_id).await;


    let mut voucher = Voucher {
        id: 0,
        sale: sale,
        distributor: distributor,
        receiver: form.to_email.to_string(),
        distributorvoucher: distributor_voucher,
        balance: balance,
        one_use_only: true,
        used: false,
        expiration_date: format!("2021-01-01 00:00:00+01:00").parse().unwrap(),
        hash_code: "hashcode".to_string(),
        number_code: "0101-020".to_string(),
    };


    data::add_voucher(&mysql, &voucher).await;


    let mut sale2 = sale;

    sale2.payment_id = payment_id;
    data::update_sale(&mysql, &sale2, data::ByIdOrPaymentId::ById(sale_id)).await;

    payment_url
}

Error

error[E0382]: use of moved value: `sale`
   --> src/main.rs:622:21
    |
588 |     let mut sale = Sale {
    |         -------- move occurs because `sale` has type `Sale`, which does not implement the `Copy` trait
...
606 |         sale: sale,
    |               ---- value moved here
...
622 |     let mut sale2 = sale;
    |                     ^^^^ value used here after move

A simple change of order won't help me I guess.

How would I do that?

You cannot make Sale: Copy unless all its field are Copy, which is not the case. Instead, see if you can #[derive(Clone)] for Sale, and then let sale2 = sale.clone() at some point before creating voucher.

(Also, marking all your variables mut like that is not good hygiene.)

3 Likes

I actually did solve it by changing orders:

let mut client = Client {
        id: 0,
        firstname: form.firstname.to_string(),
        lastname: form.lastname.to_string(),
        email: form.from_email.to_string(),
        tel: form.tel.to_string(),
        saved_account: false,
    };


    let distributor = data::get_distributor_by_subdomain(&mysql, get_subdomain_part(req.headers().get("Host").unwrap().to_str().unwrap())).await.unwrap();
    let distributor_voucher = data::get_distributor_voucher(&mysql, form.voucher).await.unwrap();

    let client_id = data::add_client(&mysql, &client).await;
    client.id = client_id;

    let mut sale = Sale {
        id: 0,
        client: client,
        payment_id: "".to_string(),
        paid: false,
    };

    let sale_id = data::add_sale(&mysql, &sale).await;
    sale.id = sale_id;

    let mut payment_id = "".to_string(); // payment_id is passed by reference and gets a new value
    //let payment_url = mollie.make_payment(&data::get_sale(&mysql, data::ByIdOrPaymentId::ById(sale_id)).await.unwrap(), &mut payment_id).await;
    let payment_url = mollie.make_payment(&sale, &mut payment_id).await;

    sale.payment_id = payment_id;
    data::update_sale(&mysql, &sale, data::ByIdOrPaymentId::ById(sale_id)).await;

    let mut voucher = Voucher {
        id: 0,
        sale: sale,
        distributor: distributor,
        receiver: form.to_email.to_string(),
        balance: distributor_voucher.amount,
        distributorvoucher: distributor_voucher,
        one_use_only: true,
        used: false,
        expiration_date: format!("2021-01-01 00:00:00+01:00").parse().unwrap(),
        hash_code: "hashcode".to_string(),
        number_code: "0101-020".to_string(),
    };


    data::add_voucher(&mysql, &voucher).await;

    payment_url

Is it good practice to add #[derive(Clone)] over all my structs? Because I'm often really getting sick of these 'move'-errors. Just spamming my code with #[derive(Clone)] would half my development duration.

It's something you should think about before doing for types that appear in the public interface of a library, or when you're using shared ownership (Rc and Arc), but in general if you find it useful then go for it. Frequent use of clone can create performance problems but you can cross that bridge when you come to it.

(Also note that some types cannot implement Clone, including anything that holds a mutable reference.)

The documentation for the Clone trait may be worth a look.

3 Likes

Thanks a lot @cole-miller and @Hyeonu for the help and explanation.
My project is compiling again and I can continue.

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.