How would you refactor use of a function from another struct?


#1

Hi, guys
Happy new year

I’m having some trouble refactoring my code. Coming from OOP, it’s been hard to resist the temptation to factor methods that belong together into different classes.

What I’m actually trying to do is in a reply below, this is what a reduced case would look like

struct test1
{
	a:bool
}

impl test1
{
	pub fn new() -> test1
	{
		test1{a:true}
	}
	
	pub fn fn1(&mut self) -> &str
	{
	"hi"
	}
	
	pub fn fn2(&self) -> &str
	{
        //I'd like to do something like mystate+=1 here so another method can perform operation depending on mystate
	"bye"
	}
}

struct test
{
	object: test1
}

impl test
{
	pub fn new() -> test
	{
		test
		{
			object:test1::new()
		}
	}
	
	fn fn2(&self, a: &str)
	{}
	
	pub fn useTest2(&mut self)
	{
		let a = self.object.fn1();
		self.fn2(a); //This is where rust doesn't let me use self again because it already has been borrowed
	}
}

fn main()
{
	let t = test::new();
	t.useTest2();
}

While I understand (very poorly) that self has been borrowed by object, so object follows the same lifetime as self so I can’t borrow it again, I can’t find a solution other than maintaining the state I wanted to maintain in struct test, which I’d like to avoid.

So for now a specific question is: How would you re-factor this?

And a more generic question is, is way of organizing code discouraged in rust?

I’m thinking in C# I could do something like

//probably not the best example because none of these really need to maintain a state
class TicketSeller
{
     Ticket ticket,
     Person salesPerson,
     etc....
}

So I can do something like

    if (salesPerson.canSellThisTicket())
    {
        salesPerson.soldATicket();
    }

So I’m wondering what the preferred way to do something like this in rust would be.

Thank you


#2

Reformatted to make it easier to read:

struct Test1 {
    a: bool,
}

impl Test1 {
    pub fn new() -> Test1 {
        Test1 { a: true }
    }

    pub fn fn1(&mut self) -> &str {
        "hi"
    }

    pub fn fn2(&self) -> &str {
        // I'd like to do something like mystate+=1 here so another method can perform operation depending on mystate
        "bye"
    }
}

struct Test {
    object: Test1,
}

impl Test {
    pub fn new() -> Test {
        Test { object: Test1::new() }
    }

    fn fn2(&self, a: &str) {}

    pub fn use_test2(&mut self) {
        let a = self.object.fn1();
        self.fn2(a); // This is where rust doesn't let me use self again because it already has been borrowed
    }
}

fn main() {
    let t = Test::new();
    t.use_test2();
}

hint: use code fences for nice formatting:

```rust ... ```

#3

To be honest, it’s kind of hard to understand what you are doing without a concrete example related of your first example.


#4

Oh, Thanks bjz
I couldn’t figure out how to do the code fences, haha.

So what I’m trying to do is an IRC server using mio
I have a connection struct which handles the connection,
I also want it to use a different struct to handle registration/logon

So I have something like this

pub struct Connection {
    // handle to the accepted socket
    sock: TcpStream,
    // token used to register with the event loop
    pub token: Token,
    // set of events we are interested in
    interest: EventSet,
    // messages waiting to be sent out
    send_queue: Vec<ByteBuf>,	
    state: ConnectionState,	
    logon_handler:logon::logon_manager
}
...
impl Connection {
      pub fn handle_input(&mut self, message: &ByteBuf) -> io::Result<bool>
      {
                       ConnectionState::New =>
			{
				//greeting
				self.state = ConnectionState::Logon;
				let buf:&[u8] = Greeting::WELCOME_MESSAGE.as_bytes();
				self.send_message(ByteBuf::from_slice(&buf)); 
			}
			ConnectionState::Logon =>
			{
				...
				let to_send = self.logon_handler.process_commands(inputString);
				self.send_message(ByteBuf::from_slice(to_send.as_bytes())); // this is where I get the error because self was already borrowed in the line above this
			},
...
      }
}

I wanted to keep the logon logic in a separate file, its logic would be based on the stage of the logon process

enum LogonState
{
	New,
	Username,
	Password,
	Register_New_User,
	Register_Password,
	Register_Password_Confirm,
        Done
}

and in my

pub fn process_commands(&mut self, cmd:String) -> &str

function, it does something like
match self.logon_state
{
LogonState::New => {},
LogonState::Username =>
{

message = &Greeting::ENTER_PASSWORD;
self.logon_state = LogonState::Password; //this is why I needed self to be mutable
},
LogonState::Password =>
{
//retrive record and compare password
self.logon_state = LogonState::Done;
}
LogonState::Register_New_User =>
{

self.logon_state = LogonState::Register_Password;
}
},
LogonState::Register_Password =>
{

message = &Greeting::CONFIRM_PASSWORD;
self.logon_state = LogonState::Register_Password_Confirm;
},
… similar logic follows
LogonState::Done => {},
}


I also plan on having another handler that would intreprete irc commands after user logs on, which is why I chose this approach.

I think I can have process_commands return LogonState and maintain that in my connections struct. I didn't quite want to do that because I feel it means connection struct would be responsible for multiple things.

However, if that is the rust way to do things, I will adopt that approach.

What do you think?

#5

I am also new to Rust. After reading 3 books about it (1 is still being written), checking most of the stackoverflow questions and reading some relevant blog posts, I also struggled for a week now to get some simple echo server built from scratch using mio. And hit similar issues like the OP.

Is there a good resource for learning Rust for people coming from the OOP background? Rust is about the 20th language I am learning in the last 25 years of development, still, I need to resort to reading source code of crates to gain any knowledge about patterns and tricks in this language.