Reference works inside match but not in their short version

Hi.
I am wondering why this happens about my code.
I am building a battleship game on console. And my struggling was to paint the points on board.
This is the code doesnt work:
I understand the error: "table has a reference, cant be moved"

//find_free_spot has reference
if let Some(result) = table.find_free_spot(){
         table.change_state(result);//signature is (&mut self)
}

But I dont understand this behaviour.

match table.can_be_road((free_spot.x, free_spot.y), spots, direction){
         Ok(points) => {
               table.change_state(points.iter().collect());
               return Ok(points);
         },
         Err(why) => continue,
};

This signature also moves change_state(& mut self) the reference, and table_can_be_road(&self) also takes an inmutable the reference. Why this happens?

In the first example, doesnt work because was moved, and second seems the same, but it works.

My idea is: when you open a scope with {} inside a match, it seems another scope or something like that.
This is not for help, because works, but I dont know why it works :=)

this is the code:

        pub fn points_from_root_point(table: & mut Table, spots: i8) -> Result<Vec<Point>, String>{
            let directions = Direction::get_directions();
            let opportunities = 10;
            for _time in 0..opportunities{
                let free_spot = match User::find_free_point(table){
                    Some(point) => point,
                    None => panic!("There are any spots free")
                };
                for direction in directions.iter(){
                    match table.can_be_road((free_spot.x, free_spot.y), spots, direction){
                        Ok(points) => {
                            table.change_state(points.iter().collect());
                            return Ok(points);
                        },
                        Err(why) => continue,
                    };
                }
            }
            return Err(format!("Any directions were sufficient to draw from root point"));
        }
//--------------------------------------------
pub fn can_be_road(&self, from: (i8,i8), spots: i8, direction: &Direction) -> Result<Vec<Point>,String>{
            let dir = direction.get_vector();            
            let points = (0..spots).map(|item| {
                let mut position = Point::new(0, 0);
                position.x = from.0 + dir.0*item;
                position.y = from.1 + dir.1*item;
                position
            }).collect::<Vec<Point>>();
            let feasible = points.iter().all(|item| self.can_put(&item));
            match feasible{
                true => Ok(points),
                false=>Err(format!("Some points in the road are not feasible to draw"))
            }
        }
//------------------------------------------------
//and this is the change_state
pub fn change_state(&mut self,points: Vec<&Point>){
            
            for point in points{
                if let Ok(point) = self.get_mut_point(point.x,point.y){
                    point.is_active = true;
                };
            }
 }
```

Could you please show more code, in particular the types and fn signatures of the variables and functions you're using?

Yes, I edited the question

In the second example, there are only borrows -- nothing is moved.

I can't tell why the first example doesn't work because you haven't shown the full error or the types.

the two eamples are not equivalent: the first calls find_free_spot(), the second calls can_be_road().

some form of if let and match CAN be interchanged, but in your example, you have also changed the type of the to-be-matched subject.

if I would guess, the find_free_spot() must have returned some type with lifetime derived from the (elided) &self parameter.

This is the code for find_free_point():

pub fn find_free_point(table : & Table) -> Option<& Point>{
            //add a table condition if all spots are active            
            let free_spots = table.space.iter().filter(|p| p.is_active == false).count();
            if free_spots == 0{
                return  None;
            }
            let free_spots = table.space.iter().filter(|p| p.is_active == false);
            let free_spots: Vec<&Point> = free_spots.collect();    
            let random_spot = rand::thread_rng().gen_range(0..free_spots.len());
            Some(free_spots.get(random_spot).unwrap())
        }

As you can see, there are only references, but when inside the let Some(val) = table.find_free_point(), you can't use methods that moves the variable.
Like change_state(&mut self), because you already has an inmutable reference inside the brackets:

if Some(val) = table.find_free_point(){
     //at this time, table is referenced in val(I think so)
     table.change_state()//which its signature is (&mut self, ...) -> doesnt work
    //because it's referenced in val, and now you cant move the value, 
    //if you have already an inmutable reference
}

If you can provide an example that works this, would be great. My rust version is rustc 1.81.0 (eeb90cda1 2024-09-04)

And the full error message for the first example?

The erros appears days ago, so I dont grab it, because I think it was normal behaviour of rust.
But now I am wondering why this works?

        pub fn points_from_root_point(table: & mut Table, spots: i8) -> Result<Vec<Point>, String>{
            let directions = Direction::get_directions();
            let opportunities = 10;
            for _time in 0..opportunities{
                if let Some(free_spot) = User::find_free_point(table){
                    for direction in directions.iter(){
                        match table.can_be_road((free_spot.x, free_spot.y), spots, direction){// *1* this takes an inmutable reference of table
                            Ok(points) => {
                                table.change_state(points.iter().collect());//*2*this needs a mut ref
                                return Ok(points);
                            },
                            Err(why) => continue,
                        };
                    }    
                }else{
                    panic!("There are any spots free")
                }
            }
            return Err(format!("Any directions were sufficient to draw from root point"));
        }

Idk, why this works, I was thinking why my mutable reference works inside my match , when the actual method takes an inmutable reference.

it is not supposed to work, because of the signature of find_free_point(), not because you used if let. I don't know what you want to do with the returned reference &Point, but just to make the code compile, you can just clone() it.

if Some(val) = table.find_free_point() {
     let val = val.clone();
     table.change_state();
}

as I said earlier, your second example doesn't do the same thing. you can change the second example into using if let, but that'll be different than the first example.

the following is equivalent to first example but use match syntax (should NOT work, just as the first case):

match table.find_free_point() {
    Some(val) => {
        table.change_state();
    }
    _ => {}
}

and the following is equivalent to the second example but use if let:

if let Ok(points) = table.can_be_road((free_spot.x, free_spot.y), spots, direction) {
     table.change_state(points.iter().collect());
     return Ok(points);
} else {
    continue;
};

Thank you, but why your second example works, when table.can_be_road() takes an inmutable reference of table, and table.change_state() takes an inmutable reference?

if let Ok(points) = table.can_be_road((free_spot.x, free_spot.y), spots, direction) {
     //at this time, table is inmutable referenced 
     table.change_state(points.iter().collect());// but now you want to a mutable reference? 
     //why this works? I have an inmutable reference and a mutable reference at the same time?

     return Ok(points);
} else {
    continue;
};

This method does not return a borrow of the table, so the table is not borrowed inside the match and therefore there is no conflict.

There may be some rule about borrowing self that you're imagining, that doesn't actually exist?

Ohh, I was thinkin this:

if let Some(val) = table.method(){
   //so table reference is valid here
   //this will not work, because table was taken by .method().
   //but somehow, table it's not referenced inside .method() anymore
   table.change_state(...)//so this work
   //and since .change_state() just take mutable reference in their scope,
   //table works at this time.
}

I think this was the behaviour, what guys do you think?

Say we have this method:

fn method(&self) -> usize {...}

When this method returns, self is no longer borrowed because the return value does not refer to self. It can't, because it is not a reference and does not contain a reference.

fn method(&self) -> &str {...}

The above method returns a reference to a str that is stored inside self. So self is still borrowed when the method returns, and for as long as the return value is in use.

If we make the elided (default) lifetimes explicit, self and the return value will have the same lifetime, which tells us that self must live for as long as the return value.

fn method(&'a self) -> &'a str {...}

But in neither case does method itself hold a reference to self somehow. So I think what you're saying below is a misunderstanding:

//but somehow, table it's not referenced inside .method() anymore

1 Like