Rustはじめました
なにしようかなーと色々眺めてたら、@mattnさんがMessageBoxでこんにちはしてたので、これをやってみることに。Windowsっ子なので。
Big Sky :: 新言語rustでhello world
use std; import std::io; #[abi = "stdcall"] native mod user32 { fn MessageBoxA(h: ctypes::c_uint, message: str::sbuf, title: str::sbuf, flag: ctypes::c_uint) -> ctypes::c_uint; } fn main() { str::as_buf("hello", { |message| str::as_buf("world", { |title| user32::MessageBoxA(0u, message, title, 0u); }) }) }
はてダのシンタックスハイライトが対応してなくて悲しくなったので、はてなブログにやってきました。幸せです。
でつい最近1.0.0のbetaが出てようやくstableになるってぐらいなので、出て間もないころに書かれた↑のコードはちっとも動かないわけです。これを動くようにします。
1.0.0betaで動くように
R:\Temp\VIE43C3.tmp.rs:2:1: 2:7 error: expected item, found `import`
http://rustbyexample.com/mod/use.html
importっぽい機能はuseに統合された模様。
R:\Temp\VIF8DBA.tmp.rs:3:18: 3:19 error: expected item after attributes R:\Temp\VIF8DBA.tmp.rs:3 #[abi = "stdcall"] R:\Temp\VIF8DBA.tmp.rs:4:1: 4:7 error: expected item, found `native` R:\Temp\VIF8DBA.tmp.rs:4 native mod user32 {
http://doc.rust-lang.org/book/ffi.html#foreign-calling-conventions
FFIはだいぶ変わってますね。ちょうどWinAPIが例に挙げられてるので真似ます。ポイントは、#[link(...)]
とextern "stdcall"
ぽい。
R:\Temp\VIGF71B.tmp.rs:15:27: 15:29 error: illegal suffix `u` for numeric literal R:\Temp\VIGF71B.tmp.rs:15 user32::MessageBoxA(0u, message, title, 0u); R:\Temp\VIGF71B.tmp.rs:15:29: 15:29 help: the suffix must be one of the integral types (`u32`, `isize`, etc)
http://rustbyexample.com/literals.html
uじゃなくてu32。でも省略しても動く気がした。
error: invalid character `.` in crate name: `VIH71C8.tmp` error: aborting due to previous error
vim-quickrunで実行したためにhogehoge.tmp.rsというファイル名になっているので、拡張子抜きから推測したcrateの名前がおかしいよって話ですね。ちなみにcreateは他の言語で言うlibraryやpackageみたいなもの、と書いてありました。
https://doc.rust-lang.org/book/crates-and-modules.html
さてどうしたものか。とりあえず今更ながらにrust-lang/rust.vimを入れてみる。だからどうなるということもない。
あきらめて一度保存した(保存済みであればtempfile使わないので)。
r:\hoge.rs:7:21: 7:35 error: failed to resolve. Use of undeclared type or module `ctypes` r:\hoge.rs:7 fn MessageBoxA(h: ctypes::c_uint,
さっきのFFIの説明を読むと、ctypeなんて書いてなかった。いらないのでは。
しかしuintはともかく、sbufの代わりになるようなものはあるのだろうか?
Replace str::sbuf with *ctypes::c_char · Issue #1715 · rust-lang/rust
ctypesはどうも消え去った気がするので、やっぱり*u8なのか。とか思ってたらlibc::c_charが例のドキュメントで使われてた。constつけときます。
r:\hoge.rs:13:7: 13:26 error: failed to resolve. Use of undeclared type or module `user32` r:\hoge.rs:13 user32::MessageBoxA(0, message, title, 0);
そりゃそうだ。
r:\hoge.rs:11:3: 11:14 error: type `str` does not implement any method in scope named `as_buf` r:\hoge.rs:11 str::as_buf("hello", { |message|
例のドキュメントではstd::ffi::CStringなるものが使われてます。newしてunwrapしてas_ptr。unsafeはC#っぽい。
r:\hoge.rs:1:1: 1:19 error: use of unstable library feature 'libc' r:\hoge.rs:1 extern crate libc; r:\hoge.rs:1:19: 1:19 help: add #![feature(libc)] to the crate attributes to enable
helpが出ていますが、今使っているのはbetaなので、
r:\hoge.rs:2:1: 2:18 error: unstable feature r:\hoge.rs:2 #![feature(libc)] note: this feature may not be used in the beta release channel error: aborting due to previous error
無理。libc::c_charじゃなくてi8使います。u8ではない。unsigned charじゃないんだからそりゃそうだろうという気もする。
やったぜ。
完成品がこちら
use std::ffi::CString; #[link(name = "user32")] extern "stdcall" { fn MessageBoxA(h: u32, message: *const i8, title: *const i8, flag: u32) -> u32; } fn main() { let message = CString::new("hello").unwrap(); let title = CString::new("world").unwrap(); unsafe { MessageBoxA(0, message.as_ptr(), title.as_ptr(), 0); } }
Rustなかなかよい。あとはてなブログなかなかよい。
追記:もうちょっといじった
#[link(name = "user32")] extern "stdcall" { fn MessageBoxA(h: u32, message: *const u8, title: *const u8, flag: u32) -> u32; } fn message_box(message: &str, title: &str) -> u32 { unsafe { return MessageBoxA(0, message.as_ptr(), title.as_ptr(), 0); } } fn main() { message_box("world!", "Hello"); }
- CString通さない版。c_charじゃないせいかu8。それに合わせてMessageBoxAの宣言変えるのも変な話だが。
- 使う側でunsafeとかあれなのでラップした。ついでに引数減らした。
- rustは最後に評価した値が戻り値になるという噂を聞いていたけど、unsafeブロックの中から返すにはreturnが必要だった。
&str
の&を取ったら、配列のサイズがわからんみたいなエラーが出た。ひょっとして文字列リテラルじゃないとこの方法使えないんだろうか。そもそも動的な文字列どう扱うんだろうね。