使用Elixir通过Ports与Rust交谈,我怎么了?

aer*_*gnr 3 port native elixir rust

我正在编写教程,因为在任何地方都找不到通过端口在Elixir和Rust之间进行通信的简单示例。

我可以让Rustler工作,但这是NIF,而不是端口。

我的代码中缺少基本的东西。我不确定我是否在stdio中缺少基本的东西,或者是否还有其他东西,但是我尝试了很多不同的东西。

可以使用Rust中的一个非常基本的程序进行端口通信:

use std::env;

fn main() {
    println!("hello world!");
}
Run Code Online (Sandbox Code Playgroud)

我可以通过运行以下端口来将其加入我的iex -S组合中:

defmodule PortExample do

  def test() do
    port = Port.open({:spawn_executable, "_build/dev/rustler_crates/portexample/debug/portexample"}, [:binary])
    Port.info(port)
    port
end
Run Code Online (Sandbox Code Playgroud)

这是该iex的样子:

defmodule PortExample do

  def test() do
    port = Port.open({:spawn_executable, "_build/dev/rustler_crates/portexample/debug/portexample"}, [:binary])
    Port.info(port)
    port
end
Run Code Online (Sandbox Code Playgroud)

我可以使用瓷器库调用执行相同的操作:

  alias Porcelain.Result
  def porcelain() do
    result = Porcelain.exec("_build/dev/rustler_crates/portexample/debug/portexample",["hello", "world"])
    IO.inspect result.out
  end
Run Code Online (Sandbox Code Playgroud)

对应的IEX:

Interactive Elixir (1.4.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> PortExample.test()
#Port<0.9420>
iex(2)> flush()
{#Port<0.9420>, {:data, "hello world!\n"}}
:ok
iex(3)> 
Run Code Online (Sandbox Code Playgroud)

但是,一旦我开始使用具有某种形式的输入/输出的Rust库,事情就会开始崩溃。

例如,Rust代码:

  alias Porcelain.Result
  def porcelain() do
    result = Porcelain.exec("_build/dev/rustler_crates/portexample/debug/portexample",["hello", "world"])
    IO.inspect result.out
  end
Run Code Online (Sandbox Code Playgroud)

我可以将其编译并在命令行中运行:

iex(3)> PortExample.porcelain()
"hello world!\n"
"hello world!\n"
iex(4)> 
Run Code Online (Sandbox Code Playgroud)

但是,当我从Elixir端口调用它时:

use std::io::{self, Write, Read};

fn main() {
    let mut input = String::new();
    let mut output = String::new();
    for i in 0..2 {
        match io::stdin().read_line(&mut input) {
            Ok(n) => {
                println!("input: {}", input.trim());
                io::stdout().flush();
            }
            Err(error) => println!("error: {}", error),
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我一点也没有得到任何数据!但是,该Port.info(port)呼叫显示其接收到15个字节。它只是没有发布过而已将任何东西返回到端口。我一直在尝试阅读其他代码,并且我认为我所做的事情足够相似,以至于它应该可以工作,但事实并非如此。

我以为:也许缓冲区没有刷新?所以我在Rust中刷新缓冲区。我以为:也许循环正在挂起,所以我只将其限制为几遍。当我尝试通过瓷器调用运行相同的代码时,它挂起了。

Dog*_*ert 5

您正在读取Rust代码中的输入行,该行将一直读取到\r\n或为止\n,但您不是从Elixir发送换行符。如果您更改所有Port.command呼叫以\n在消息后添加a ,则该方法有效:

iex(1)> port = Port.open({:spawn_executable, "a"}, [:binary])
#Port<0.1229>
iex(2)> Port.command(port, "hello")
true
iex(3)> flush()
:ok
iex(4)> Port.command(port, "hello\n")
true
iex(5)> flush()
{#Port<0.1229>, {:data, "input: hellohello\n"}}
:ok
Run Code Online (Sandbox Code Playgroud)