0

I have the following Connection struct and a simple constructor function:

struct Connection;

impl Connection {
   pub fn new() -> Connection {
      // before constructor
      let construct  = Connection;
      // after constructor
      construct
   }
}

I want to be able to register events that happen before/after creation of any Connection. For example.

register!(Connection, before, println!("Before 1"));
register!(Connection, before, println!("Before 2"));
register!(Connection, after, println!("After"));

So once I call Connection::new() it should at least attempt to write:

//out: Before 1
//out: Before 2 
returns value
//out: After 

I think this requires a static Observable class, but is that even possible in safe Rust?

Daniel Fath
  • 16,453
  • 7
  • 47
  • 82

2 Answers2

4

It's possible, but it's not built-in to the language. You get to understand every nuance of such a decision:

mod connection {
    pub struct Connection;

    impl Connection {
        fn new() -> Connection {
            Connection
        }
    }

    pub struct ConnectionFactory {
        befores: Vec<Box<Fn()>>,
        afters: Vec<Box<Fn()>>,
    }

    impl ConnectionFactory {
        pub fn new() -> ConnectionFactory {
            ConnectionFactory {
                befores: Vec::new(),
                afters: Vec::new(),
            }
        }

        pub fn register_before<F>(&mut self, f: F)
            where F: Fn() + 'static
        {
            self.befores.push(Box::new(f))
        }

        pub fn register_after<F>(&mut self, f: F)
            where F: Fn() + 'static
        {
            self.afters.push(Box::new(f))
        }

        pub fn build(&self) -> Connection {
            for f in &self.befores { f() }
            let c = Connection::new();
            for f in &self.afters { f() }
            c
        }
    }
}

use connection::*;

fn main() {
    let mut f = ConnectionFactory::new();
    f.register_before(|| println!("Before 1"));
    f.register_before(|| println!("Before 2"));
    f.register_after(|| println!("After"));

    f.build();

    // Connection::new(); // error: method `new` is private
}

The important thing is that Connection::new is no longer public and the only way of constructing one is through a ConnectionFactory. That factory is what holds the closures that you need. Of course you can change the closure signature to do more useful stuff like returning a boolean to abort creation.

If it's important to you to be able to catch every possible construction, then you must make a global mutable singleton.

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
0

AFAIK it's not possible without changing how the user interacts with the Library type.

First of all: there are no "real" constructors like in an OOP sense, like in C++. In this code:

struct Foo { 
    x: i32,
    y: bool,
}

let f = Foo {   // <-- this
    x: 0,
    y: true,
};

... the marked spot is just a struct initializer. It just assigns values and does not execute arbitrary code. In your code the ... = Connection; part is this kind of simple initializer.

Functions like new, with_capacity and default are only simple associated (or "static") functions and nothing special either.

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
  • I meant can ensure that nothing will ever access struct with anything other than `::new()` like constructor. I was going for **minimal** example, which means I'm not going to write full struct declaration. – Daniel Fath Jan 22 '16 at 17:16