hiro99ma blog

Something technical

rust: Rustがんばろう 13日目

2025/04/12

はじめに

まだトレイトの話に至っていなかった。
10.2章がトレイトなので、今回こそは・・・!

10.1章の続き

enumunion

何言ってんだって感じだが、複数の型が同じメモリを所有してどれかの型だけが意味を持つという意味であれば C言語の union はそういう振る舞いをする。
まあ、C言語だとバイナリ的な扱いをするためにビットフィールドと組み合わせることが多いが。 エンディアンとかあるので CPU 依存になるけど、まあそういうものだよね。 心配なら #if で該当プラットフォーム以外は #error にしておけばよいのだ。

selfは2番目には書けない

fn mixup<V, W>(self, other: Point<V, W>) とあったので、そういえば self は 2番目に書けるんだっけ?と気になった。

結果、書けない。
だよね。 そもそも self は引数として明記するものではないし。

だいたい self も書かせなくてもよいのではないのかと思ったが、self&self で挙動が違う。
確かに List 10-11 の最後にある p1.mixup(p2) より後では p1 を参照できなかった。

このサンプルでは &self の方がよかったんじゃないかとも思ったが p2 も所有権を奪うので意図的なのだろう。

ジェネリクスを使用したコードのパフォーマンス

コンパイラは、ジェネリックなコードが呼び出されている箇所全部を見て、 ジェネリックなコードが呼び出されている具体的な型のコードを生成するのです

C++ のテンプレートと同じ挙動だと思う。
つまり、型の分だけコードが生成されるのだろう。

例えば List 10-11 のバイナリを nm で見てみる。
ここでバイナリ名が hello なのは、私がずっと hello を龍称しているからだ。

$ nm target/debug/hello | grep mixup
00000000000079d0 t _ZN5hello18Point$LT$T$C$U$GT$5mixup17h2e9fabcca742e05cE

ここに、p1, p2, p3 に出てこなかった Point<f64, i32>p4 を作って p3.mixup(p4) してみよう。

struct Point<T, U> {
    x: T,
    y: U,
}

impl<T, U> Point<T, U> {
    fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

fn main() {
    let p1 = Point { x: 5, y: 10.4 };
    let p2 = Point { x: "Hello", y: 'c'};

    let p3 = p1.mixup(p2);

    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);

    let p4 = Point { x: 5.0, y: 10};
    let p5 = p3.mixup(p4);

    println!("p5.x = {}, p5.y = {}", p5.x, p5.y);
}

このバイナリを nm で見るとシンボルが増えている。

$ nm target/debug/hello | grep mixup
00000000000079d0 t _ZN5hello18Point$LT$T$C$U$GT$5mixup17h2e9fabcca742e05cE
0000000000007a00 t _ZN5hello18Point$LT$T$C$U$GT$5mixup17h8a9ab73e1ce52e3cE

まあ、仕方ないのだ。 こうするしかないのだ。

コード量を増やさずにジェネリックを使いたかったら、 ジェネリックでどういう型を使うかを把握しながら作業すれば良いだけだ。
別に Rust だけでなく他の言語でも同じだろう。

最近は ROM というかストレージというか、そういうメモリは比較的安価なので増やしやすい。
ちょっと増やすことでソフトウェアの工数を減らせるなら安いものだ。。。と思いたい。

でも工場とか生産技術的なところが一番力が強いので、 生産数が見込めないと他の製品で使っているのと同じ部品を使いなさいってなるかもしれない。
勝手に FLASH のビット数を増やした部品に交換されて「動かない」って言われたときはどうしてやろうかと思ったけどね!!
まあ、初めてのときにチェックが甘くて違う工場のラインを止めたことがあるのでチャラだ(チャラではありません。すごく怒られた)。
(今思うと、事前チェックせずに受け入れたってこと? 違うよね。事前チェックして動作しなかったのでラインの組み替えをしないといけなかったというお叱りだったのだと思う。記憶にないけどすまん。)

nm で demangling するとこうだ。

$ nm --demangle target/debug/hello | grep mixup
00000000000079d0 t hello::Point<T,U>::mixup
0000000000007a00 t hello::Point<T,U>::mixup

気にしたことなかったけど $LT$T$C$U$GT$LT = <, $C = ,, $GT = > なのか。
Point<A, B> にすると hello::Point<A,B>::mixup になった。

では、と Point<A,C> にすると hello::Point<A,C>::mixup になる。
demangling 前は hello::Point$LT$A$C$C$GT だったので、コンマの $C と型の $Cで区別はないのか。

なら Point<A, CC> だとどうなるかというと Point$LT$A$C$CC$GT だった。
名前の付け方のルールまでは調べないが、リンクするときの目印だから深く考えまい。

おわりに

なんか過去の思い出したくない記憶まで引きずり出されたが、次回からトレイトの話になりそうだ。


 < Top page


コメント(Google Formへ飛びます)

GitHub

X/Twitter

Homepage