Help us understand the problem. What is going on with this article?

Rustのconst fnでabs

以前の記事でRustのconst fnの制限が厳しいという話を書いた。
正直使い所がわからないが悔しかったのでいろいろ試していたら、条件によって返す値を変えることくらいはできた。

Rust 1.31.0で確認しています。


追記: 2019/11/06
Rust 1.39.0で標準のabsconst fnになりました。
https://qiita.com/block/items/646c725289a8431383f9


追記: 2020/08/29
Rust 1.46.0でifなどが使用できるようになりabsは通常の関数と同じように書けるようになったため、おまけ3として追加しました。
緩和された内容はRustのconst fnの制限がさらに緩和された(Rust 1.46.0)に書きました。

absの実装

const fn abs(v: i32) -> i32 {
    [-v, v][(v >= 0) as usize]
}

解説

[-v, v]
符号を反転した値ともとの値の配列を作る。

[(v >= 0) as usize]
vが0以上かどうかで配列のインデックスを変更する。
(v >= 0)の結果がboolなのでusizeにキャストするとtrueなら1を、falseなら0を返す。
つまりtrueであれば[-v, v][1]となりvを返し、falseであれば[-v, v][0]となり-vを返す。

何故こんな実装にしなければならないのか

絶対値を返すだけで何故こんなにも分かりづらい形にしなければならないのか。
まずconst fnではifが使えない。
そのため下記はコンパイルエラーになる

const fn abs(v: i32) -> i32 {
    if v >= 0 {
        v
    } else {
        -v
    }
}

インデックスだけでも変数に代入すれば多少はわかりやすくなりそうだが変数が使えないので下記はコンパイルエラー

const fn _abs(v: i32) -> i32 {
    let index: usize = (v >= 0) as usize; // 変数は使えないのでコンパイルエラー
    [-v, v][index]
}

const fnの中でも定数の定義はできるが変数を使用して定数の定義はできないので下記もコンパイルエラー

const fn _abs(v: i32) -> i32 {
    const INDEX: usize = (v >= 0) as usize; // 変数を使用して定数の定義はできないのでコンパイルエラー
    [-v, v][INDEX]
}

変数を作ることなくアクセスする返す値を変更したいため、今回の形になった。

ビット演算で符号を変更すればいいのでは?

そうですね。多分できると思います。

追記: 2019/11/06
標準のabsはビット演算してる。
https://qiita.com/block/items/646c725289a8431383f9

まとめ

絶対値を返すだけでとても苦労する。
条件によって返す値は変えられたのでもしかしたら多少複雑な処理はできるかもしれない。
可読性は犠牲になりそう。

おまけ

別パターン。 
配列を作らなくても良い方法。

const fn abs(v: i32) -> i32 {
    v * (((0 <= v) as i32) * 2 - 1)
}

おまけ2

1.33.0でconst fnの制限が緩和されたので可読性を上げる。
途中で書いていたindexを変数に代入するやつです。

const fn _abs(v: i32) -> i32 {
    let index = (v >= 0) as usize;
    [-v, v][index]
}

おまけ3

1.46.0でifが使えるようになったので通常の関数のように書くことができます。

const fn abs(value: i32) -> i32 {
    if value >= 0 {
        value
    } else {
        -value
    }
}
block
Game/Game Programming/C++/Rust/C#
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away