0

Minimal Reproducible Example

pub struct User { pub id: i32, pub name: String, pub match_id: i32, } pub struct Match { pub id: i32, pub name: String, } pub struct MatchWithUsers { pub id: i32, pub name: String, pub users: Vec<User>, } fn main() { let query_result: Vec<(Match, Option<User>)> = vec![ ( Match { id: 1, name: String::from("1st match"), }, Some(User { id: 1, name: String::from("Jack"), match_id: 1, }), ), ( Match { id: 2, name: String::from("2nd match"), }, Some(User { id: 2, name: String::from("John"), match_id: 2, }), ), ( Match { id: 3, name: String::from("3rd match"), }, None, ), ]; let mut response: Vec<MatchWithUsers> = Vec::new(); for (m, u) in &query_result { let existing_match = &response .into_iter() .find(|match_with_user| match_with_user.id == m.id); match existing_match { Some(found_match) => { println!("Inser user into match: {}", found_match.name); match u { Some(mut user) => { found_match.users.push(user); } None => println!("No users."), } } None => { println!("No existing match. Add to response."); let user = u.as_ref().unwrap(); response.push(MatchWithUsers { id: m.id, name: m.name.clone(), users: vec![], }); } } } println!("Response with: {}", response.len()); } 
warning: unused variable: `user` --> src/main.rs:69:21 | 69 | let user = u.as_ref().unwrap(); | ^^^^ help: consider prefixing with an underscore: `_user` | = note: `#[warn(unused_variables)]` on by default warning: variable does not need to be mutable --> src/main.rs:61:26 | 61 | Some(mut user) => { | ----^^^^ | | | help: remove this `mut` | = note: `#[warn(unused_mut)]` on by default error[E0382]: use of moved value: `response` --> src/main.rs:53:31 | 50 | let mut response: Vec<MatchWithUsers> = Vec::new(); | ------------ move occurs because `response` has type `std::vec::Vec<MatchWithUsers>`, which does not implement the `Copy` trait ... 53 | let existing_match = &response | ^^^^^^^^ value moved here, in previous iteration of loop error[E0507]: cannot move out of `u.0` which is behind a shared reference --> src/main.rs:60:23 | 60 | match u { | ^ 61 | Some(mut user) => { | -------- | | | data moved here | move occurs because `user` has type `User`, which does not implement the `Copy` trait error[E0596]: cannot borrow `found_match.users` as mutable, as it is behind a `&` reference --> src/main.rs:62:25 | 62 | found_match.users.push(user); | ^^^^^^^^^^^^^^^^^ `found_match` is a `&` reference, so the data it refers to cannot be borrowed as mutable 

Playground

My problem

I have an API test project using Rocket and Diesel.

The following method does a Diesel query and should map the result to a JSON response. This is a response of all matches in the database with thier users. The users should be nested in each Match node.

My solution attempt

  1. I create a vector.
  2. Iterate in the query result;
  3. Check if the match already exists in my vector, if it does, add the user info (coming from the query result) and add it to the users attribute in the current MatchWithUser, otherwise just add a struct (MatchWithUsers) to the vector.
pub fn show_all_matches2() -> Vec<MatchWithUsers> { use schema::*; let connection = establish_connection(); let query_result: Vec<(Match, Option<User>)> = matches::table .left_join(users::table.on(users::match_id.eq(matches::id))) .load(&connection) .expect("Error loading matches"); let mut response: Vec<MatchWithUsers> = Vec::new(); for (m, u) in &query_result { let existing_match = &response .into_iter() .find(|match_with_user| match_with_user.id == m.id); match existing_match { Some(mut found_match) => { println!("Inser user into match: {}", found_match.name); found_match.users.push(u.unwrap()); } None => { println!("No existing match. Add to response."); let user = u.as_ref().unwrap(); response.push(MatchWithUsers { id: m.id, name: m.name.clone(), players_count: m.players_count, users: vec![User { id: user.id, name: user.name.clone(), match_id: user.match_id, }], }); } } } response } 

Structs

use crate::schema::{matches, users}; use serde::{Deserialize, Serialize}; #[derive(Queryable, Identifiable, Associations, Serialize, Deserialize)] #[belongs_to(Match)] #[table_name = "users"] pub struct User { pub id: i32, pub name: String, pub match_id: i32, } #[derive(Queryable, Identifiable, Serialize, Deserialize)] #[table_name = "matches"] pub struct Match { pub id: i32, pub name: String, pub players_count: i32, } #[derive(Serialize, Deserialize)] pub struct MatchWithUsers { pub id: i32, pub name: String, pub players_count: i32, pub users: Vec<User>, } 

Errors

errors when compiling

$ cargo run Compiling got_board_api_v3 v0.1.0 (/Users/tauil/Projects/got/got-board-api-v3) error[E0382]: use of moved value: `response` --> src/lib.rs:54:31 | 51 | let mut response: Vec<MatchWithUsers> = Vec::new(); | ------------ move occurs because `response` has type `std::vec::Vec<models::MatchWithUsers>`, which does not implement the `Copy` trait ... 54 | let existing_match = &response | ^^^^^^^^ value moved here, in previous iteration of loop error[E0507]: cannot move out of `existing_match.0` which is behind a shared reference --> src/lib.rs:58:15 | 58 | match existing_match { | ^^^^^^^^^^^^^^ 59 | Some(mut found_match) => { | --------------- | | | data moved here | move occurs because `found_match` has type `models::MatchWithUsers`, which does not implement the `Copy` trait error[E0507]: cannot move out of `*u` which is behind a shared reference --> src/lib.rs:61:40 | 61 | found_match.users.push(u.unwrap()); | ^ | | | move occurs because `*u` has type `std::option::Option<models::User>`, which does not implement the `Copy` trait | help: consider borrowing the `Option`'s content: `u.as_ref()` 

Query result in the Postgres client:

select * from matches left join users on matches.id = users.match_id; id | name | players_count | id | name | match_id ----+---------------------+---------------+----+-----------+---------- 1 | My first match | 3 | 1 | Rafael | 1 1 | My first match | 3 | 2 | Leandro | 1 1 | My first match | 3 | 3 | Vagner | 1 2 | Just a second match | 4 | 4 | Vagner | 2 2 | Just a second match | 4 | 5 | Leandro | 2 2 | Just a second match | 4 | 6 | Rafael | 2 2 | Just a second match | 4 | 7 | Wanderson | 2 3 | Amazing match | 6 | | | (8 rows) 
9
  • Instead of a Vec, you should consider using a HashMap<i32, MatchWithUsers>, which would be much more efficient when looking for duplicates, then use the Entry API to insert or modify the stored value. Commented Mar 20, 2020 at 7:23
  • @Jmb Thanks for the feedback and answer. I've updated the question with the error text. I'll also give it a try to HashMap. I had seen some examples with it but I it's acctually a bit different from what I wanted to do initially. Commented Mar 23, 2020 at 16:57
  • It looks like your question might be answered by the answers of Does Rust have a way to apply a function/method to each element in an array or vector?. If not, please edit your question to explain the differences. Otherwise, we can mark this question as already answered. Commented Mar 23, 2020 at 17:00
  • It's hard to answer your question because it doesn't include a minimal reproducible example, an emphasis on minimal. It would make it easier for us to help you if you try to reproduce your error on the Rust Playground if possible, otherwise in a brand new Cargo project, then edit your question to include the additional info. There are Rust-specific MRE tips you can use to reduce your original code for posting here. Thanks! Commented Mar 23, 2020 at 17:01
  • Please paste the error as text in the question, not as an image. This way other people who face the same problem later will be able to search for it. Commented Mar 24, 2020 at 7:36

1 Answer 1

1

You can get a list of matches with the users associated with each match very easily if you use group_by from the itertools crate and if you ensure that your query results are sorted by match ID:

use itertools::Itertools; // 0.9.0 let response: Vec<_> = query_result .into_iter() // Note that this assumes that `query_result` is sorted // by match id since `group_by` only considers // consecutive matches. .group_by(|(m, _)| m.id) .into_iter() .map(|(id, mut g)| { // Now `g` is an iterator of `(Match, Option<User>)` // where all the matches are the same. We take the // first item to get the match information. Note // that it is safe to unwrap here because `group_by` // would never call us with an empty `g`. let (m, u) = g.next().unwrap(); MatchWithUsers { id: id, name: m.name, // We got the first user along with the match // information, now we append the other users // from the remaining items in `g`. users: u .into_iter() .chain(g.flat_map(|(_, u)| u.into_iter())) .collect(), } }) .collect(); 

Playground

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.