2017-05-18 3 views
2

Je suis en train d'écrire un tutoriel, parce que je n'ai trouvé aucun exemple simple de communication entre Elixir et Rust via un port.Utiliser Elixir pour parler à Rust via Ports, qu'est-ce que je me trompe?

Je peux faire fonctionner Rustler, mais c'est un NIF, pas un port.

Il me manque quelque chose de fondamental dans mon code. Je ne sais pas si je manque quelque chose de basique dans stdio ou si c'est quelque chose d'autre, mais j'ai essayé beaucoup de choses différentes.

Je peut obtenir du port de communication pour travailler avec un programme très basique à Rust:

use std::env; 

fn main() { 
    println!("hello world!"); 
} 

Je peux obtenir ce pour obtenir tiré dans mon IEX S mix en exécutant ce port:

defmodule PortExample do 

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

Voici ce que IEX pour qui ressemble à:

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)> 

je peux faire la même chose en utilisant un appel de la bibliothèque de porcelaine:

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

IEX correspondants:

iex(3)> PortExample.porcelain() 
"hello world!\n" 
"hello world!\n" 
iex(4)> 

Cependant, dès que je commence à utiliser une bibliothèque Rust avec une certaine forme d'entrée/sortie, les choses commencent tomber.

Par exemple, le code Rust:

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), 
     } 
    } 
} 

je peux l'obtenir pour compiler et exécuter dans la ligne de commande:

hello 
input: hello 
world 
input: hello 
world 

Cependant, quand je l'appelle d'un port Elixir:

iex(12)> port = PortExample.test() 
#Port<0.8779> 
iex(13)> Port.command(port, "hello") 
true 
iex(14)> Port.command(port, "world") 
true 
iex(15)> Port.command(port, "!")  
true 
iex(16)> Port.command(port, "more") 
true 
iex(17)> flush() 
:ok 
iex(18)> Port.info(port) 
[name: '_build/dev/rustler_crates/portexample/debug/portexample', 
links: [#PID<0.271.0>], id: 4697, connected: #PID<0.271.0>, input: 0, 
output: 15, os_pid: 21523] 

Je ne reçois aucune donnée du tout! Cependant, l'appel Port.info(port) montre qu'il a reçu 15 octets. Il n'a tout simplement pas posté quoi que ce soit retourné au port. J'ai essayé de lire d'autres codes et j'ai pensé que je faisais des choses assez similaires pour que cela fonctionne, mais ce n'est pas le cas.

J'ai pensé: peut-être que le tampon n'est pas vidé? alors je vide le tampon dans Rust. J'ai pensé: peut-être que la boucle est suspendue, donc je l'ai limitée à seulement quelques passages. Lorsque j'essaie d'exécuter ce même code à travers l'appel de porcelaine, il se bloque.

Répondre

4

Vous lisez une ligne d'entrée dans le code Rust, qui va lire jusqu'à \r\n ou \n, mais vous n'envoyez pas le caractère de nouvelle ligne à Elixir. Si vous changez tous les appels Port.command d'ajouter un \n après le message, il fonctionne:

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 
+0

Merci! C'est tout. Je savais que ça devait être une chose fondamentale qui me manquait. – aeroegnr