1. elipmoc101

    No comment

    elipmoc101
Changes in body
Source | HTML | Preview
@@ -1,208 +1,272 @@
#概要
Rustの標準マクロはかゆいとこに手が届く!
Rustのマクロはいいぞ!
――というわけで。Rustで標準で用意されているマクロでも紹介したいと思いまする。
#Rust標準マクロのソースコード
Rustのマクロは一つのファイルに全部まとめられている。
ドキュメントから見れます
https://doc.rust-lang.org/src/std/macros.rs.html
一部のマクロはコンパイラマジックだったりして、見てるだけでけっこう面白い。
#Rust便利マクロ一覧
早速やっていきましょう。
あ、面白くないのは飛ばすね。
##compile_error!
panic!は実行時にエラーを出すけど、こいつはコンパイル時にエラーを出すことができるマクロだ。
コンパイル時にコードに ”compile_error!(なんか文字列);” が含まれていると即座にコンパイルが失敗するよ。
自作マクロで好ましくない引数を渡されたときにエラーを出させたりとかできる。
適当に例を書いた
```rust
macro_rules! foo {
() => {
compile_error!("式を渡してね!");
};
($e:expr) => {};
}
foo!();
```
これを実行するとfoo!();の部分がcompile_error!("式を渡してね!");に置き換わり、コンパイルエラーとなる。
エラー内容はこんな感じ
```
error: 式を渡してね!
--> src\hoge.rs:13:9
|
13 | compile_error!("式を渡してね!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
18 | foo!();
| ------- in this macro invocation
```
##env!
env!は環境変数とかの値を取得するためのマクロだ。
実行時にではなく、コンパイル時に値が置き換えられる点に注意。
型は文字列リテラルだと思います。
使い方
```rust
env!("PATH");
env!("SystemRoot");
```
あと、こいつを使えばCargoが定義している環境変数も取得できる。
ここでどんな値があるか書いてる。
https://doc.rust-lang.org/cargo/reference/environment-variables.html
例えば、cargo.exeの場所が知りたい時はenv!("CARGO")とすると、
~~/cargo.exeみたいにフルパスで取得できる。
Cargoで定義されている環境変数は自作ライブラリのbuildscript書くときとかに重宝する。
##option_env!
先程のenv!には少し問題があって、env!("存在しない環境変数名")みたいに無い環境変数を指定するとコンパイルエラーになる。
もし、環境変数がなかったら、こちらの処理に分岐する。っといった処理ができないのです。
そこでoption_env!です。
こいつはOption<&'static str>型で値を返します。
つまり、環境変数が見つからなくても、コンパイルエラーにならずに、Option::Noneを返すだけとなります。便利。
##concat_idents!
これは、識別子を結合します。
識別子というのがポイントです!文字列を結合するわけではありません!
公式から引っ張ってきた例です。
```rust
fn foobar() -> u32 { 23 }
let f = concat_idents!(foo, bar);
println!("{}", f());
```
concat_idents!(foo,bar)はコンパイル時にfoobarに置き換えられているのがわかりますね。
concat_idents!(foo,bar,baz)のように複数繋げれます。
残念ながら現在はnightly版のRustでしか動かないようです。
##concat!
これはコンパイル時に渡された値を結合して文字列に置き換えるマクロです
```rust
//foo10truebarに置き換わる!
concat!("foo", 10, true, "bar");
```
文字列以外も結合対象にできます。面白いですね。
どうやら、リテラルなら基本結合できそうです。
##line!
このマクロは、展開されると、そのマクロが位置している行番号へ置き換わります。
型は符号なし整数リテラルです。
```rust
fn main(){
let hoge:u32 = line!(); //hoge=2
let huga:u32 = line!(); //huga=4
}
```
##column!
これはさっきのline!の列番号バージョンです
##file!
これは、このマクロが配置されているファイルのパスに文字列リテラルで置き換わります。
フルパスではなく、プロジェクトからの相対パスっぽい。
##stringify!
これは良いものです。痒いところに手が届く!
まあそれは置いといて。
このマクロは任意の何かをそのままで文字列にしてしまうやつです。
```rust
stringify!(1+1); // "1+1"
stringify!(foobar); // "foobar"
stringify!(file!()); // "file ! ( )"
// "fn main ( ) { println ! ( "{}" , 1 + 2 ) }"
stringify!(fn main(){println!("{}",1+2 )})
```
##include_str!
これもなかなかユニークで面白い。
コンパイル時にファイルに書かれている文字列に置き換えてくれます。
事前に適当にファイルを用意します。
```txt:hoge.txt
abcd
efgh
マクロサイコー!
```
そしてこうです!
```rust
const s: &str = include_str!("hoge.txt");
fn main() {
println!("{}", s);
}
```
これで、hoge.txtが出力されます。
自分自身のソースコードを出力とかもできる。
##include_bytes!
include_str!のbytes版です。
##module_path!
現在位置しているモジュールまでのモジュールパスをコンパイル時に文字列に展開します。
```rust
mod foo{
pub mod bar{
pub fn baz(){
println!(module_path!());
}
}
}
fn main() {
foo::bar::baz();
}
/* project_name::foo::bar が表示される*/
```
##include!
これもなかなかおもしろい。
include_str!はファイルの内容を文字列リテラルで展開しますが、
これはそのまま展開します。
C言語のincludeに似ていますね。
```rust:hello.rs
println!("hello!")
```
```rust
fn main(){
include!("hello.rs");
}
```
コンパイルするとinclude!("hello.rs")がprintln!("hello!")に置き換わり、
実行するとhello!が出力されます。
+##try!
+正直これは、チュートリアルにも乗っているから、書くかどうか迷ったんですが、とても便利なマクロですので、紹介します。
+一応チュートリアルのURLも貼っておこう。
+http://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/error-handling.html
+try!はResultの中身の取り出しをunwrapより安全に、かつパターンマッチを書かず簡潔に行えます。
+
+例を見せたほうが早いだろう。
+
+```rust
+fn foo() -> Result<i32, ()> {
+ Result::Ok(777)
+}
+
+fn bar() -> Result<i32, ()> {
+ let num = try!{foo()};
+ println!("{}", num);
+ Ok(num)
+}
+
+fn main() {
+ bar();
+}
+
+/* 777が表示される */
+```
+このようにして、try!を使うと中身を簡単に取り出せます。
+unwrapと違うところは、値がErrだとしてもpanic!にはならずにreturnされるところ。
+
+正確ではないが、さっきのbar関数のtry!の部分を置き換えるとこうなる。
+
+```rust
+fn bar() -> Result<i32, ()> {
+ let num = match foo() {
+ Ok(x) => x,
+ Err(x) => return Err(x),
+ };
+ println!("{}", num);
+ Ok(num)
+}
+
+```
+正確なtry!マクロの詳細についてはこちらのソースコードを参照するといい
+https://doc.rust-lang.org/src/core/macros.rs.html#363-371
+
+しかし、ここまで紹介しておいてなんだがtry!マクロを使うことは少ないかも知れない。
+それは次に説明する。
+
+##?演算子(オマケ)
+
+先程、try!マクロを紹介したが、これを簡略化した演算子が?演算子です。
+この演算子はtry!の糖衣構文で式をカッコで囲む手間をなくすことができます。
+
+try!で見せたbar関数を?演算子で書き換えた例はこう。
+
+```rust
+fn bar() -> Result<i32, ()> {
+ let num = foo()?;
+ println!("{}", num);
+ Ok(num)
+}
+
+```
+
+
#おわり