やっつけな日常(48) RustにいればRustに従え、融通の利くprintln!

Joseph Halfmoon

Rustは型に厳格といいつつ「意外と融通を利かせて」くれるような気が(個人の感想っす。)それはコンパイラが厳格に管理している(変なことはできない)ために可能な技?なのかもしれませぬが。今回は「融通が利く」println!と「融通が利かない」assert_eq!を比べながら実験してみたいと思います。どちらもマクロだね。

※「やっつけな日常」投稿順 Index はこちら

※今回参照させていただいとります Rust のドキュメントは以下あたりです。

Macro std::println

Macro std::assert_eq

実験に使用したソース全文と実行結果

短いので先にソース全文掲げておきます。

fn main() {
    let s = 111;
    let sr = &s;
    let sb = Box::new(111);
    let sr2 = &s;
    let sr3 = &111;

    println!("s :   {}", s);
    println!("sr:   {}", sr);
    println!("sb:   {}", sb);
    println!("sr2:  {}", sr2);
    println!("sr3:  {}", sr3);

    let ssum = s + sr + *sb;
    println!("ssum: {}", ssum);

    assert_eq!(111, s,  "111 != s");
//    assert_eq!(111, sr, "111 != &s");
    assert_eq!(&111, sr, "&111 != &s");
//    assert_eq!(111, sb, "111 != sb");
    assert_eq!(Box::new(111), sb, "Box(111) != sb");
    assert!(std::ptr::eq(sr, sr2));
    assert!(std::ptr::eq(sr3, sr2), "ERROR!");
}

そして上記を実行したものが以下に。末尾のassert!がコケているので最後にパニックが起こってますが、それ以前は正常に通過しとります。

printlnResult

 

実験結果の吟味

最初に、整数値111を値として持つ5個の変数を定義してます。sは単純なi32型の変数。srはsの参照。sbはヒープ上のBox内に111を詰めたもの。sr2はもう1個のsの参照。sr3はsでなく整数111の参照とな。

let s = 111;
let sr = &s;
let sb = Box::new(111);
let sr2 = &s;
let sr3 = &111;

年寄の心に染みついている C であれば、*ptr と書くべきところに ptr などと書いてしまったらダメダメな結果になるにきまっているのです。しかし、太っ腹で融通が利く Rust のprintln!マクロでは、以下のような異なる変数でも、みな 111 だたと印字してくれるのでした。

println!("s : {}", s);
println!("sr: {}", sr);
println!("sb: {}", sb);
println!("sr2: {}", sr2);
println!("sr3: {}", sr3);

便利っちゃ便利だけれども、年寄はチト落ち着きませぬ。

さらにいうと、上記の変数のうち、sとsrとsbの値を足し算しようとするとこんな感じに書けます。

let ssum = s + sr + *sb;
println!("ssum: {}", ssum);

単純変数 s と参照 sr はそのまま足し合わせ可能。しかしBoxに入れてある sb についてはデリファレンス * して値を取り出せばOKっと。

C頭の年寄には、この辺の挙動がなれませぬな。

println!に比べると「厳格に」比較を行ってくれるassert_eq!マクロにお願いすると差が見えてきます。

assert_eq!(111, s, "111 != s");
// assert_eq!(111, sr, "111 != &s");
// assert_eq!(111, sb, "111 != sb");

上記の最初の行は単純変数 s を数値 111 に比べて成功するのですが、2行目と3行目はコンパイルが通りません。srはリファレンスだし、sbはBox型なのでダメだと拒絶されます。println! じゃ、どちらも 111 だと印字してくれるのに。

Box型は同じ値を含むBox型と比べてやれば比較一致します。

assert_eq!(Box::new(111), sb, "Box(111) != sb");

リファレンスは、Rawポインタ同士、同じ実体数値を指していれば比較成功します。

assert!(std::ptr::eq(sr, sr2));

リファレンスの比較では、当然ですが、等価な数値を指していても異なるRawポインタの比較であれば、不成功となります。エラー、パニック。

assert!(std::ptr::eq(sr3, sr2), "ERROR!");

C頭で忘却力の年寄は頭の切り替えが追い付かない??

やっつけな日常(47) RustにいればRustに従え、イテレータで使えるメソッドその3 へ戻る

やっつけな日常(49) rapinfo、このコマンドなんだっけ? RaspberryPi へ進む