foreign_class!

foreign_class! is the way to describe an entity that will be visible for the "foreign language" as a class.

The basic example is:

foreign_class!(class Foo {
    self_type Foo;
    constructor Foo::new(_: i32) -> Foo;
    fn Foo::set_field(&mut self, _: i32);
    fn Foo::f(&self, _: i32, _: i32) -> i32;
    fn f2(_: i32) -> i32;
});

Here Foo may be struct or enum or a more complex type (see below). The self_type can be omitted if there are no constructors and all fn do not accept self.

self_type

The self_type is used as a "neat" way to call methods of types inside "smart pointers". For example sometimes you can not use your struct or enum explicitly, because of the semantic of the "foreign language" does not allow it, then you can write:

foreign_class!(class CircularDepsA {
    self_type CircularDepsA;
    constructor CircularDepsA::java_new(_: &str) -> Arc<Mutex<CircularDepsA>>;
    fn CircularDepsA::a(&self, b: &CircularDepsB) -> String;
});

And flapigen will be instructed to generate code to call each method listed above with lock().unwrap().method syntax.

Also maybe you want export a trait implementation as foregin_class. The flapigen cannot work with Box<Trait>, because it requires unstable/unsafe APIs to convert the boxed trait into something that can be transferred over the FFI border, but it can work with Box<Box<Trait>>, so via self_type you can represent a boxed trait as class:

pub trait Interface {
    fn f(&self, _: i32) -> i32;
    fn set(&mut self, _: i32);
}
struct InterfaceImpl {
    base: i32,
}

impl Interface for InterfaceImpl {
    fn f(&self, x: i32) -> i32 {
        self.base + x
    }
    fn set(&mut self, x: i32) {
        self.base = x;
    }
}

fn create_interface() -> Box<Box<dyn Interface>> {
    Box::new(Box::new(InterfaceImpl { base: 17 }))
}

foreign_class!(class Interface {
    self_type dyn Interface;
    constructor create_interface() -> Box<Box<dyn Interface>>;
    fn Interface::f(&self, _: i32) -> i32;
    fn Interface::set(&mut self, x: i32);
});

Also it should be noted that flapigen generates code that uses "Universal Function Call Syntax", so methods can be fake methods and declared outside of impl blocks:

pub struct TestMethodNotMethod;

impl TestMethodNotMethod {
    fn new() -> Self {
        TestMethodNotMethod
    }
}

fn method_not_method(_this: &TestMethodNotMethod) {}

foreign_class!(class TestMethodNotMethod {
    self_type TestMethodNotMethod;
    constructor TestMethodNotMethod::new() -> TestMethodNotMethod;
    fn method_not_method(&self);
});

Access modifiers

By default, methods in generated class are public. You can change access level via protected and private keywords.

foreign_class!(
    class Foo {
        self_type Foo;
        constructor Foo::new() -> Foo;
        private constructor Foo::from_int(_: i32) -> Foo;
        private fn Foo::private_f();
        fn Foo::public_f();
        protected fn Foo::protected_f();
    }
);

Inline methods

It is also sometimes convenient to write short methods just inside foreign_class!, for example to add a way to access fields of a struct or to export a result of a Rust macro call:

foreign_class!(class TestFnInline {
    fn int_to_str(a: i32) -> String {
        format!("{}", a)
    }
});

To reference self in inline methods you should use the this variable, because the inline method is not the real one, it is just code block that gets included into the generated code:

pub struct Session {
    name: String,
}

foreign_class!(
    #[derive(SmartPtrCopy)]
    class Session {
        self_type Session;
        constructor session_init(name: &str) -> Rc<RefCell<Session>> {
            Rc::new(RefCell::new(Session {
                name: name.into(),
            }))
        }
        fn name(&self) -> &str {
            &this.name
        }
    }
);

Methods aliases

Also you can create an alias for a function name:

    fn Foo::f(&self, _: i32, _: i32) -> i32;  alias calcF;

So it would be called by path with fn in Rust code, and you can call it via name with alias in foreign language.

This may be useful for example if you want to name functions in Java in camel case style, while you want to use snake case style in Rust.

Constructors

Constructors are Rust methods that mapped to constructors in terms of the "foreign" language. Also constructors, more precisely, the return type of constructors can be used to ask flapigen to simplify calls of methods, see the self_type section for more details.

If you define constructor that uses default function, like constructor Foo::default() -> Arc<Foo>;, then we generate call to Arc::<Foo>::default().

Sometimes you need a constructor, but you don't want allow the user to construct objects, then you can use an empty constructor:

foreign_class!(
    class GamepadId {
        self_type GamepadId;
        private constructor = empty;
        fn GamepadId::value(&self) -> usize;
    }
);

foreigner_code

flapigen also supports bypassing code generation:

foreign_class!(class TestPathAndResult {
    self_type TestPathAndResult;
    constructor TestPathAndResult::empty() -> Result<TestPathAndResult, String>;
    constructor TestPathAndResult::new(path: &Path) -> Result<TestPathAndResult, String>;
    fn TestPathAndResult::get_path(&self) -> String; alias getPath;
    fn TestPathAndResult::get_boo(&self) -> Rc<RefCell<Boo>>; alias getBoo;
    foreign_code "    public int javaFunc() { return 17; }\n";
    foreign_code r#"
    public Boo[] testHandArrayReturn() { return do_testHandArrayReturn(this.mNativeObj); }
    private static native Boo[] do_testHandArrayReturn(long me);
"#;
    fn TestPathAndResult::get_foo_list(&self) -> Vec<Foo>;
    fn TestPathAndResult::get_result_foo_list(generate_err: bool) -> Result<Vec<Foo>, String>;
});

After that you can implement the Java_com_example_TestPathAndResult_do_1testHandArrayReturn function yourself, useful for when flapigen cannot handle something automatically, or you want to do something special.

Doc comments

You can also add comments to generated code with Rust doc comments:

foreign_class!(
/// Class comment description for Foo.
#[derive(Clone)]
class Foo {
    self_type Foo;
    /// some text about the new function
    ///
    /// ```
    /// some markdown example in the text
    /// ```
    ///
    /// @param val - some number
    /// @param name - more information
    constructor Foo::new(val: i32, name: &str) -> Foo;

Derives

You can use "derive" syntax on the declaration of classes, in a similar way to the usage on "Rust" structs:

/// Class comment description for Foo.
#[derive(Clone)]
class Foo {
    self_type Foo;

Usage of derives changes generated code in various ways. For example, you can use Clone,Copy to force the generation of a copy constructor and operator= in C++ case. You can also use camelCaseAliases to change names of all methods to camel case.

SmartPtrCopy

You can use #[derive(SmartPtrCopy)] when a foreign class's constructor returns Rc<...> or Arc<...>. Flapigen clones the smart pointer when another owner needs the value; it does not clone the Rust object inside it.

In C++, it generates a copy constructor and copy assignment operator. The C++ example has a counter backed by Rc<RefCell<_>>:

pub struct SharedCounter {
    value: i32,
}

impl SharedCounter {
    fn new() -> std::rc::Rc<std::cell::RefCell<Self>> {
        std::rc::Rc::new(std::cell::RefCell::new(Self { value: 0 }))
    }

    fn increment(&mut self) {
        self.value += 1;
    }

    fn value(&self) -> i32 {
        self.value
    }
}
foreign_class!(
    #[derive(SmartPtrCopy)]
    class SharedCounter {
        self_type SharedCounter;
        constructor SharedCounter::new() -> Rc<RefCell<SharedCounter>>;
        fn SharedCounter::increment(&mut self);
        fn SharedCounter::value(&self) -> i32;
    }
);
    SharedCounter counter;
    SharedCounter second_handle = counter; // clones Rc, not SharedCounter
    second_handle.increment();
    SharedCounter assigned;
    assigned = counter;
    assigned.increment();
    if (counter.value() != 2 || second_handle.value() != 2) {
        return EXIT_FAILURE;
    }

counter, second_handle, and assigned are three C++ handles to the same Rust counter. Incrementing through either copy changes the value read through counter.

In Java, passing a wrapper by value to Rust clones its smart pointer, so the Java wrapper remains usable and Rust can retain the shared value. The Android example stores an Arc<Mutex<Session>> in another Rust object:

struct SessionStore {
    session: Arc<Mutex<Session>>,
}

impl SessionStore {
    fn new(session: Arc<Mutex<Session>>) -> Self {
        Self { session }
    }

    fn saved_base(&self) -> i32 {
        self.session.lock().unwrap().a
    }
}
foreign_class!(#[derive(SmartPtrCopy)] class Session {
    self_type Session;
    constructor Session::new() -> Arc<Mutex<Session>>;
    fn Session::add_and1(&self, val: i32) -> i32;
    fn Session::set_base(&mut self, base: i32);
    fn Session::greet(to: &str) -> String;
});

foreign_class!(class SessionStore {
    self_type SessionStore;
    constructor SessionStore::new(session: Arc<Mutex<Session>>) -> SessionStore;
    fn SessionStore::saved_base(&self) -> i32;
});
        Session session = MyApplication.get().getSession();
        final int res = session.add_and1(2);
        SessionStore store = new SessionStore(session); // Rust retains an Arc clone
        session.set_base(7); // the Java wrapper remains usable
        final int sharedBase = store.saved_base();
        tv_number.setText(getString(R.string.rust_results_text, res, sharedBase));
        store.delete();

The store observes changes made through the original Session. This derive does not add a Java copy constructor; its effect is on ownership when the wrapper is passed to Rust.