Method chaining

Hi,

I've been studying Rust for some months and now, to consolidate the knowledge I'm trying to implement a simple query builder using a method chaining pattern.

The test I did is working, but I need to break the chaining to run the test.
The current code:

    #[test]
    fn should_store_equal_constraint() {
        let mut query = Query::new()
            .from("table1")
            .select_range(vec!["columnName1", "columnName2"]);

        query    
            .and_equal("columnA", 35);

        let mut i = 1;
        for column in query.columns {
            if let Statement::StringValue(tb) = &column.value {
                assert_eq!(*tb, String::from(format!("columnName{}", i)));
            } else {
                panic!("Unknown TableContainer");
            }
            i += 1;
        }
    }

How I want it:

    #[test]
    fn should_store_equal_constraint() {
        let query = Query::new()
            .from("table1")
            .select_range(vec!["columnName1", "columnName2"])
            .and_equal("columnA", 35);

        let mut i = 1;
        for column in query.columns {
            if let Statement::StringValue(tb) = &column.value {
                assert_eq!(*tb, String::from(format!("columnName{}", i)));
            } else {
                panic!("Unknown TableContainer");
            }
            i += 1;
        }
    }

The errors cargo test is showing:

error[E0716]: temporary value dropped while borrowed
   --> src/query/mod.rs:237:25
    |
237 |           let mut query = Query::new()
    |  _________________________^
238 | |             .from("table1")
239 | |             .select_range(vec!["columnName1", "columnName2"])
    | |_____________________________________________________________^ creates a temporary which is freed while still in use
240 |               .and_equal("columnA", 35);
    |                                        - temporary value is freed at the end of this statement
...
243 |           for column in query.columns {
    |                         ------------- borrow later used here
    |
    = note: consider using a `let` binding to create a longer lived value

error[E0507]: cannot move out of `query.columns` which is behind a shared reference
   --> src/query/mod.rs:243:23
    |
243 |         for column in query.columns {
    |                       ^^^^^^^^^^^^^ move occurs because `query.columns` has type `Vec<column::Column>`, which does not implement the `Copy` trait
    |
help: consider iterating over a slice of the `Vec<column::Column>`'s content
    |
243 |         for column in &query.columns {
    |        

Feel free to look at my repository or ask for more information. I'm struggling with this for weeks and I've changed it sometimes, but now I'm stuck.

Thanks in advance.

There's the tap crate which can be useful to inspect objects without breaking method chaining. Though IMO it's fine to break chaining.

You are probably trying to take and return &mut Self from builder functions. You should be taking self and returning Self instead, if you want the final call to return by-value.

1 Like

I also tried this approach:

    pub fn and_equal<T, Y>(mut self, left_value: Y, right_value: T) -> Query
    where T: ToStatement, Y: ToStatement {
        self.constraints.push(
            Constraint::and_equal(left_value, right_value)
        );
        self
    }

But if I break the chaining (a behavior that I want to keep too), it shows me another error:

error[E0382]: use of moved value: `query.columns`
   --> src/query/mod.rs:243:23
    |
235 |         let mut query = Query::new()
    |             --------- move occurs because `query` has type `query::Query`, which does not implement the `Copy` trait
...
240 |             .and_equal("columnA", 35);
    |              ------------------------ `query` moved due to this method call
...
243 |         for column in query.columns {
    |                       ^^^^^^^^^^^^^ value used here after move
    |
note: this function takes ownership of the receiver `self`, which moves `query`
   --> src/query/mod.rs:102:32
    |
102 |     pub fn and_equal<T, Y>(mut self, left_value: Y, right_value: T) -> Query
    |  

That's the trade-off of having a by-value builder - you can't just call the methods on their own without re-assigning. You'd need to do:

query = query.and_equal("columnA", 35);

Or collapse that and_equal into the original chain:

let query = Query::new()
            .from("table1")
            .select_range(vec!["columnName1", "columnName2"])
            .and_equal("columnA", 35);
3 Likes