Method calls with : &self.xx

Hi,

I am having a problem resolving the following situation:

 struct Obj {
        aa: i32,
        bb: i32,
        cc: String
}

/*
trait A {         // I tried to move it into trait
	
	fn get_bb(self,r:i32)-> i32;
	
}*/

        
impl Obj {
        fn add (a: i32, b: i32)-> i32 {
            a+b
        }
        fn mlt (a: i32, b: i32)-> i32 {
            a*b
        }
        fn make (a: i32, b: i32)-> Obj {
            Obj {
                aa:  Obj::mlt(a,b),
                bb:  Obj::add(a,b),
                cc:  "yey".to_string()
            }
        }
        fn get_aa(self,r:i32)-> i32{   // I cannot reference self in this one
            self.aa 
        }
        fn get_bb(&self,r:i32)-> i32{
            self.bb
        }
}
   
/*
impl A for Obj {                     // I tried to move it into trait 
       fn get_bb(self,r:i32)-> i32{
            self.bb
        }
}
*/

fn main() {

    let x : i32 = 6;
    let v : i32 = 8;
    let k: i32 = 67;
    let ss  = Obj::make(x,v);

    println!("{}:{}", ss.get_aa(k),ss.get_bb(k));


}

The error I am getting is :

 --> src/main.rs:55:36
   |
55 |     println!("{}:{}", ss.get_aa(k),ss.get_bb(k));
   |                       --           ^^ value borrowed here after move
   |                       |
   |                       value moved here
   |
   = note: move occurs because `ss` has type `Obj`, which does not implement the `Copy` trait

I cannot add Copy due to String (in a real example I have std::fs::File inside my Obj as well as another structure that cannot be cloned - at least this is what complier says)
Moreover, get_aa() cannot be referenced because it is an iterator: My previous post I tried to play around with the second method but no luck. Is there a way to make my calls println!("{}:{}", ss.get_aa(k),ss.get_bb(k)); work somehow without messing with get_aa() method ?

Thank you for your patience !

You simply need to call the functions in a different order.

Yes that resolves my clumsy example :slight_smile: Thnx!!

But what if I cannot change the order :frowning: ? How would I deal with it then ?

Well, you don't. get_aa says "You can't use the Obj after this", and the compiler dutyfully enforces this. If you can't change get_aa to only use a reference, there's no way for you to do this (I'm not fully sure why "it is an iterator" would keep you from using a reference, but that's certainly dependent on the exact thing you're doing). Maybe you can provide a more complete example of what you're trying to do with what?

1 Like

Sorry for the delay. Here is a more concrete situation I am trying to implement:

use std::fs::File;
use std::io::{BufReader, BufRead};

pub struct Obj {
    fh: File,
    cc: String
}

impl Obj {
    fn make(f: &str) -> Obj {
        Obj {
            fh : File::open(f).unwrap(),
            cc: f.to_string(),           
        }
    }
    fn records(self) -> impl Iterator<Item = String> {
        BufReader::new(self.fh)
            .lines()
            .map(Result::unwrap)
            .filter(|s| !s.contains("blah"))
    }
    fn get_cc(self)-> String{
            self.cc
        }
}
   

fn main() {

    let ss  = Obj::make("test.txt");
         
    let a = ss.get_cc();
    println!("{}", a);
    for record in ss.records() {
		println!("{}:{}", a, record);
    }
}

Error I get:

error[E0382]: use of moved value: `ss`
  --> src/main.rs:38:19
   |
36 |     let a = ss.get_cc();
   |             -- value moved here
37 |     println!("{}", a);
38 |     for record in ss.records() {
   |                   ^^ value used here after move
   |
   = note: move occurs because `ss` has type `Obj`, which does not implement the `Copy` trait

And I cannot implement Copy .... any ideas?

Can you start with let records = ss.records().collect::<Vec<_>>()? Or they're too large to load all at once?

too large :frowning:

can you add a fn get_aa_bb(self) -> (A, B) ?

1 Like

Understandingly Obj cannot imply Copy and records must consume self. But is there anything forbidding get_cc to receive a &self reference?

    fn get_cc(&self)-> String{
            self.cc.clone()
    }

If there are several methods that require to move self and do not return it then you only can call one of them. So you need either all but one of them to receive references, perhaps by using a unique method to gather all the information (as @dcarosone propose), or to return self when not allowing references (although I cannot find now a good reason to do this).

    fn weird_cc(self) -> (String, Self){
        (self.cc.clone(), self)
    }
4 Likes

Allowing fn get_cc to receive a &self should solve the problems like so:

fn get_cc(&self) -> String {
         self.cc.to_string()
    }

Without passing the &self reference, the value would have moved giving ownership to the variable a. I agree with @nakacristo totally.

1 Like

can you add a fn get_aa_bb(self) -> (A, B) ?

To be more concrete for your second example, this would be get_cc_records() returning a tuple of self.cc and the iterator.

I have assumed that the real code is such that cloning what you've represented here as String for cc is infeasible/undesirable.

Basically, you only get to consume the thing once, so you need to return both parts at once (as above), or you need to progressively destructure it, like a reverse builder pattern (demolition pattern?). A bit like @nakacristo did in weird_cc(), but without clone() you need to give ownership of self.cc separately, removing it from the struct before returning the rest. That would mean cc would be an Option<Foo> in the struct, and you use take() to remove it and pass it back separately.

    fn extract_cc(self) -> (Option<String>, Self){
        (self.cc.take(), self)
    }

This extract_cc() could probably also be done taking a &mut self and returning just the Option<String>.

    fn extract_cc(&mut self) -> Option<String> {
        self.cc.take()
    }
2 Likes