Shogo's Blog

Feb 25, 2017 - 2 minute read - polyglot rust rustlang go golang

Rust vs Go の終戦へ向けてPolyglotを作ってみた

「Golang Rust」とググると、関連項目は「Rust vs Go」のように GolangとRustが対立しているような項目ばかりです。 まあまあ、もっと仲良くやろうじゃないですか、ということで、 どうしたら仲良くなれるかを考えました。 Polyglotにして同じソースコードの中に閉じ込めてやれば、 そのうち仲良くなるのではないかと考え、 RustとGoのPloyglotを作ってみました。

結果

/*/*/
package main

import "fmt"

func main() {
	fmt.Print("Hello Go!!")
	_ = `*/*/
fn main() {
    println!("Hello Rust!!");
//`
}
/*/*/
package main

import "fmt"

func main() {
	fmt.Print("Hello Go!!")
	_ = `*/*/
fn main() {
    println!("Hello Rust!!");
//`
}

仕組み

一番のポイントは最初の行の /*/*/ です。 RustもGoも/* */形式の複数行コメントに対応していますが、 Rustはネストに対応しており、Goはネストはできないという違いがあります。 この違いにより、Rustは/*/*//* /* /のように「二重にネストしたコメントの開始部分」として扱いますが、 Goは/* / */のように「/をコメントアウトしたもの」と見なします。 これにより2行目package main以降はGoには普通のコードに見えますが、 Rustからは単なるコメントとして認識されます。

次はGoからRustへの切り替えです。 Goではバッククオートで複数行文字列を定義できるので、その中にRustのコードを書きます。 この中ではバッククオートさえ使わなければ自由にRustのコードを書くことが出来るので、 あとはGoのコードだけ上手くコメントアウトされるよう調整すれば完成です。

せっかくなのでリンクしてみた

GoからRustのコードを呼び出すサンプルコードを見つけたので、 せっかくなのでリンクしてみました。

main.golib.goを以下のように置き換えます。 内容は一緒なので、シンボリックリンクにすると編集が楽でいいかもしれませんね。

/*golang code starts from here/*/
package main

/*
#cgo LDFLAGS: -L./lib -lhello
void hello(char *name);
*/
import "C"

func main() {
	C.hello(C.CString("John Smith"))

	_ = `rustlang code starts from here */*/
extern crate libc;
use std::ffi::CStr;

#[no_mangle]
pub extern "C" fn hello(name: *const libc::c_char) {
    let buf_name = unsafe { CStr::from_ptr(name).to_bytes() };
    let str_name = String::from_utf8(buf_name.to_vec()).unwrap();
    println!("Hello {}!", str_name);
//`
}
/*golang code starts from here/*/
package main

/*
#cgo LDFLAGS: -L./lib -lhello
void hello(char *name);
*/
import "C"

func main() {
	C.hello(C.CString("John Smith"))

	_ = `rustlang code starts from here */*/
extern crate libc;
use std::ffi::CStr;

#[no_mangle]
pub extern "C" fn hello(name: *const libc::c_char) {
    let buf_name = unsafe { CStr::from_ptr(name).to_bytes() };
    let str_name = String::from_utf8(buf_name.to_vec()).unwrap();
    println!("Hello {}!", str_name);
//`
}

呼び出し元と呼び出し先のコードが一度に確認できて便利(?)

まとめ

Goの最初にpackage mainを書かなければいけない制限が意外と厳しいため、 Polyglotにする言語には相性があります。 つまりRustとGoは相性バツグンということですね!(???) みなさんもRustとGoを仲良く使っていきましょう!!!!!

(※ジョークなので本気にしないでくださいね、念のため)

参考