Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New rustler::init! errors #310

Closed
CharlesOkwuagwu opened this issue May 5, 2020 · 11 comments
Closed

New rustler::init! errors #310

CharlesOkwuagwu opened this issue May 5, 2020 · 11 comments

Comments

@CharlesOkwuagwu
Copy link

Please i get this error trying to translate the following to new rustler syntax:

previous:

rustler_export_nifs!(
    "Elixir.Xtdlib.Native",
    [
        ("new", 0, new),
        ("send", 2, send),
        ("execute", 2, execute),
        ("recv", 2, receive, SchedulerFlags::DirtyIo),
    ],
    Some(on_load)
);
error: proc macro panicked
  --> src/lib.rs:91:1
   |
91 | / rustler::init!(
92 | |     "Elixir.Xtdlib.Native",
93 | |     [new, send, execue, recv],
94 | |     Some(load)
95 | | );
   | |__^
   |
   = help: message: expected assignment expression (i.e. `load = load`)

I'm not sure how to proceed.

@evnu
Copy link
Member

evnu commented May 5, 2020

Adapted from the tests:

rustler::init!(
    "Elixir.Xtdlib.Native",
    [new, send, execue, recv],
    load = load
);

Can you give this a try?

@scrogson I think our code example in rustler_codegen is wrong, see here:

/// rustler::init!("Elixir.Math", [add, sub, mul, div], Some(load));

@CharlesOkwuagwu
Copy link
Author

@evnu Thanks for that.

Please we need better examples and directions for moving to the new syntax. for example:

  1. can we combine both old and new syntax?
  2. how do we return a general type like any or Term?
fn receive<'a>(env: Env<'a>, args: &[Term<'a>]) -> Result<Term<'a>, Error> {
    let tdlib: ResourceArc<XTdlib> = args[0].decode()?;
    let timeout: f64 = args[1].decode()?;

    match tdlib.tdlib.receive(timeout) {
        None => Ok(atoms::null().encode(env)),
        Some(resp) => Ok((atoms::ok(), resp).encode(env)),
    }
}

my attempt:

#[rustler::nif(schedule = "DirtyIo", name = "recv")]
fn receive(tdlib: ResourceArc<XTdlib>, timeout: f64) -> Term {
    tdlib.tdlib.receive(timeout)
}

@evnu evnu added the needs docs label May 5, 2020
@evnu
Copy link
Member

evnu commented May 5, 2020

can we combine both old and new syntax?

Not in rustler::init(), but for the NIFs themselves you can use the old syntax with the new macro attribute. See further below.

how do we return a general type like any or Term?

#[rustler::nif(schedule = "DirtyIo", name = "recv")]
fn receive(tdlib: ResourceArc<XTdlib>, timeout: f64) -> Term {
    tdlib.tdlib.receive(timeout)
}

I assume this fails with a type error?

NIFs with the new syntax can either return a type which can be converted automatically, or you can still return plain-old NifResult and encode manually. See this test for example:

pub fn unowned_to_owned<'a>(env: Env<'a>, binary: Binary<'a>) -> NifResult<Binary<'a>> {
    let mut copied = binary.to_owned().unwrap();
    copied.as_mut_slice()[0] = 1;
    Ok(copied.release(env))
}

In the case of your receive, if the type of tdlib.tdlib.receive(timeout) is not complicated to write out, you could give this a try (I assume the return type of receive() is Response):

#[rustler::nif(schedule = "DirtyIo", name = "recv")]
fn receive(tdlib: ResourceArc<XTdlib>, timeout: f64) -> Option<Response> {
    tdlib.tdlib.receive(timeout)
}

With regard to converting to the new syntax, I would propose to do that piece-by-piece: prefix your NIFs with #[rustler::nif(..)] and keep everything else as is. If I am not mistaken, that should work to get it all compiling again and your tests to work. Then you can start to refactor as you see fit.

@CharlesOkwuagwu
Copy link
Author

@evnu Thanks for your assistance, but now it just throws a bunch of errors ...

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate rustler;
extern crate rust_tdlib;

use rustler::resource::ResourceArc;
use rustler::schedule::SchedulerFlags;
use rustler::{Encoder, Env, Error, Term};

use rust_tdlib::Tdlib;

mod atoms {
    rustler::atoms! {
        ok,
        error,
        null,
    }
}

struct XTdlib {
    tdlib: Tdlib,
}

unsafe impl Sync for XTdlib {}
unsafe impl Send for XTdlib {}

fn on_load(env: Env, _info: Term) -> bool {
    rustler::resource!(XTdlib, env);
    true
}

#[rustler::nif]
fn new<'a>(env: Env<'a>, _args: &[Term<'a>]) -> Result<Term<'a>, Error> {
    let tdlib = Tdlib::new();
    let resource = ResourceArc::new(XTdlib { tdlib: tdlib });
    Ok((atoms::ok(), resource).encode(env))
}

#[rustler::nif]
fn send<'a>(env: Env<'a>, args: &[Term<'a>]) -> Result<Term<'a>, Error> {
    let tdlib: ResourceArc<XTdlib> = args[0].decode()?;
    let request: &str = args[1].decode()?;

    tdlib.tdlib.send(request);
    Ok(atoms::ok().encode(env))
}

#[rustler::nif]
fn execute<'a>(env: Env<'a>, args: &[Term<'a>]) -> Result<Term<'a>, Error> {
    let tdlib: ResourceArc<XTdlib> = args[0].decode()?;
    let request: &str = args[1].decode()?;

    match tdlib.tdlib.execute(request) {
        None => Ok(atoms::null().encode(env)),
        Some(resp) => Ok((atoms::ok(), resp).encode(env)),
    }
}

#[rustler::nif(schedule = "DirtyIo", name = "recv")]
fn receive<'a>(env: Env<'a>, args: &[Term<'a>]) -> Result<Term<'a>, Error> {
    let tdlib: ResourceArc<XTdlib> = args[0].decode()?;
    let timeout: f64 = args[1].decode()?;

    match tdlib.tdlib.receive(timeout) {
        None => Ok(atoms::null().encode(env)),
        Some(resp) => Ok((atoms::ok(), resp).encode(env)),
    }
}

// rustler_export_nifs!(
//     "Elixir.Xtdlib.Native",
//     [
//         ("new", 0, new),
//         ("send", 2, send),
//         ("execute", 2, execute),
//         ("recv", 2, receive, SchedulerFlags::DirtyIo),
//     ],
//     Some(on_load)
// );

rustler::init!(
    "Elixir.Xtdlib.Native",
    [new, send, execue, recv],
    load = load
);

compile - errors

error[E0433]: failed to resolve: use of undeclared type or module `recv`
  --> src/lib.rs:84:26
   |
84 |     [new, send, execute, recv],
   |                          ^^^^ use of undeclared type or module `recv`

error[E0425]: cannot find value `load` in this scope
  --> src/lib.rs:85:12
   |
85 |     load = load
   |            ^^^^ not found in this scope

warning: unused `#[macro_use]` import
 --> src/lib.rs:1:1
  |
1 | #[macro_use]
  | ^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused `#[macro_use]` import
 --> src/lib.rs:3:1
  |
3 | #[macro_use]
  | ^^^^^^^^^^^^

warning: unused import: `rustler::schedule::SchedulerFlags`
 --> src/lib.rs:8:5
  |
8 | use rustler::schedule::SchedulerFlags;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `&[rustler::Term<'a>]: rustler::Decoder<'_>` is not satisfied
  --> src/lib.rs:33:1
   |
33 | #[rustler::nif]
   | ^^^^^^^^^^^^^^^ the trait `rustler::Decoder<'_>` is not implemented for `&[rustler::Term<'a>]`
   |
   = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `&[rustler::Term<'a>]: rustler::Decoder<'_>` is not satisfied
  --> src/lib.rs:40:1
   |
40 | #[rustler::nif]
   | ^^^^^^^^^^^^^^^ the trait `rustler::Decoder<'_>` is not implemented for `&[rustler::Term<'a>]`
   |
   = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `&[rustler::Term<'a>]: rustler::Decoder<'_>` is not satisfied
  --> src/lib.rs:49:1
   |
49 | #[rustler::nif]
   | ^^^^^^^^^^^^^^^ the trait `rustler::Decoder<'_>` is not implemented for `&[rustler::Term<'a>]`
   |
   = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `&[rustler::Term<'a>]: rustler::Decoder<'_>` is not satisfied
  --> src/lib.rs:60:1
   |
60 | #[rustler::nif(schedule = "DirtyIo", name = "recv")]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `rustler::Decoder<'_>` is not implemented for `&[rustler::Term<'a>]`
   |
   = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 6 previous errors

Some errors have detailed explanations: E0277, E0425, E0433.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `xtdlib_native`.

@evnu
Copy link
Member

evnu commented May 5, 2020

error[E0433]: failed to resolve: use of undeclared type or module `recv`
  --> src/lib.rs:84:26
   |
84 |     [new, send, execute, recv],
   |                          ^^^^ use of undeclared type or module `recv`

Your function is called receive. You must use the Rust name here, not the rename (the rename is for Elixir/Erlang).

error[E0425]: cannot find value `load` in this scope
  --> src/lib.rs:85:12
   |
85 |     load = load
   |            ^^^^ not found in this scope

Your function is called on_load, so this must be load = on_load.

error[E0277]: the trait bound `&[rustler::Term<'a>]: rustler::Decoder<'_>` is not satisfied
  --> src/lib.rs:60:1
   |
60 | #[rustler::nif(schedule = "DirtyIo", name = "recv")]
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `rustler::Decoder<'_>` is not implemented for `&[rustler::Term<'a>]`
   |
   = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

This is caused by the function arguments:

#[rustler::nif(schedule = "DirtyIo", name = "recv")]
fn receive<'a>(env: Env<'a>, args: &[Term<'a>]) -> Result<Term<'a>, Error> {
    let tdlib: ResourceArc<XTdlib> = args[0].decode()?;
    let timeout: f64 = args[1].decode()?;

    match tdlib.tdlib.receive(timeout) {
        None => Ok(atoms::null().encode(env)),
        Some(resp) => Ok((atoms::ok(), resp).encode(env)),
    }
}

Rustler is not able to decode args. You don't need to decode manually anymore; try to convert your functions like this:

#[rustler::nif(schedule = "DirtyIo", name = "recv")]
fn receive<'a>(env: Env<'a>, tdlib: ResourceArc<XTdlib>, timeout: f64) -> Result<Term<'a>, Error> {
    match tdlib.tdlib.receive(timeout) {
        None => Ok(atoms::null().encode(env)),
        Some(resp) => Ok((atoms::ok(), resp).encode(env)),
    }
}

(@scrogson is there a backwards compatible way to make the old function syntax work? I don't know if the changelog is explicit enough wrt to this)

@scrogson
Copy link
Member

scrogson commented May 5, 2020

is there a backwards compatible way to make the old function syntax work?

@evnu no, there is not. The new macros do not work with the slice of terms.

@CharlesOkwuagwu
Copy link
Author

Please how do we conditionally return {:ok, some_string} | :ok in the new 0.22 syntax?

#[rustler::nif]
fn send(tdlib: ResourceArc<XTdlib>, request: &str) -> Atom {
    tdlib.tdlib.send(request);

    atoms::ok()
}

#[rustler::nif]
fn execute(tdlib: ResourceArc<XTdlib>, request: &str) -> Term {
    match tdlib.tdlib.execute(request) {
        None => atoms::ok(),      <-- complains here
        Some(resp) => (atoms::ok(), resp),
    }
}
mismatched types

expected struct `rustler::Term`, found struct `rustler::Atom`rustc(E0308)
lib.rs(52, 17): expected struct `rustler::Term`, found struct `rustler::Atom`

@filmor
Copy link
Member

filmor commented Jun 26, 2020

Call .encode(env) on the results (and include the env parameter). This is an "issue" with the way types work in Rust, can't really be fixed in Rustler:

None => atoms::ok().encode(env),
Some(resp) => (atoms::ok(), resp).encode(env)

actix-web solves this using an Either type that implements the HttpResponse trait, maybe that would be an idea here as well?

@CharlesOkwuagwu
Copy link
Author

@filmor Thanks.

Please i'm still not clear on this.

These were the old format:

use rustler::{Atom, Encoder, Env, NifResult, Term};
...

fn send<'a>(env: Env<'a>, args: &[Term<'a>]) -> Result<Term<'a>, Error> {
    let tdlib: ResourceArc<ErlTdlib> = args[0].decode()?;
    let request: &str = args[1].decode()?;

    tdlib.tdlib.send(request);
    Ok(atoms::ok().encode(env))
}

fn execute<'a>(env: Env<'a>, args: &[Term<'a>]) -> Result<Term<'a>, Error> {
    let tdlib: ResourceArc<ErlTdlib> = args[0].decode()?;
    let request: &str = args[1].decode()?;

    match tdlib.tdlib.execute(request) {
        None => Ok(atoms::null().encode(env)),
        Some(resp) => Ok((atoms::ok(), resp).encode(env)),
    }
}

This is what i'm trying now, with the added .encode(env)

use rustler::{Atom, Encoder, Env, NifResult, Term};
...

#[rustler::nif]
fn send(tdlib: ResourceArc<XTdlib>, request: &str) -> Atom {
    tdlib.tdlib.send(request);

    atoms::ok()
}

#[rustler::nif]
fn execute(tdlib: ResourceArc<XTdlib>, request: &str) -> Term {
    match tdlib.tdlib.execute(request) {
        None => atoms::ok().encode(env),
        Some(resp) => (atoms::ok(), resp).encode(env),
    }
}

Please kindly help me clarify.

@scrogson
Copy link
Member

In your execute function you need to pass in env: Env as the first argument.

@CharlesOkwuagwu
Copy link
Author

oh, ok. thanks @scrogson

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants