50582


変数の値をミュータブルやイミュータブルに変更できる言語はRust以外にも数多くあると思いますが、これらの言語でイミュータブルな変数を外部から書き換える場合は、どのような挙動を起こすのでしょうか？「外部から書き換える」とはRustで表すとこんな感じです。

[code]

    //スケッチ1
    fn main() {
    let x = 1;
    println!("変数の値は{}", x);
    x = 2; //ここで直接ではなく、外部から書き換えている。
    println!("新し変数の値は{}", x);
    }
    
[/code]

公式ドキュメントによると、

> ＜中略＞なぜなら、この状況はまさしく、バグに繋がるからです。コードのある部分は、
> 値が変わることはないという前提のもとに処理を行い、別の部分がその値を変更していたら、
> 最初の部分が目論見通りに動いていない可能性があるのです。このようなバグの発生は、
> 事実(訳注:実際にプログラムを走らせた結果のことと思われる)の後には追いかけづらいものです。 特に第2のコード片が、値を時々しか変えない場合尚更です。
>
> 参照URL（[https://doc.rust-jp.rs/book/second-edition/ch03-01-variables-and-
> mutability.html#a変数と可変性](https://doc.rust-jp.rs/book/second-
> edition/ch03-01-variables-and-
> mutability.html#a%E5%A4%89%E6%95%B0%E3%81%A8%E5%8F%AF%E5%A4%89%E6%80%A7)）

という解説がなされていていますが、少々わからない事があります。というのも私自身が設計思想上ゆるい部類の言語であるRubyでプログラムの組まれ方学んだ人間なので、そもそもミュータブル・イミュータブルを採用した言語への理解が進んでいないというのが現状で、よって、

> 値が変わることはないという前提のもとに処理を行い、別の部分がその値を変更していたら、 最初の部分が目論見通りに動いていない可能性があるのです。

の記述のイメージが少々つきにくい部分があります。これらを踏まえてお伺いしたいことをリストアップすると、

  1. 変数のミュータブル・イミュータブルを採用した言語で（イメージとしての）上記のようなスケッチを実行させると、どのような結果が出るのでしょうか？
  2. 変数のミュータブル・イミュータブルを採用したコンパイル言語で、上記のスケッチをコンパイルして、それが通った場合どのような不具合が出るのでしょうか（言語名も挙げてもらえると嬉しいです。）？


CC BY-SA 4.0
2018-11-23T04:41:19.627
50558
2018-11-24T04:32:08.763
30493
question
rust プログラミング言語
Rust以外の言語でイミュータブルな変数の値を書き換えると、どのような挙動を起こすのか？


「外部から書き換える」がどういうコードを指しているのか想像できないのですが、一応、以下のような理解で回答しますね。

  * 外部から書き換えるコード → 書き換えが禁止されているイミュータブルな変数を書き換えられる裏技的なコード

以下の言語での経験をもとに書きます。

  * Rust
  * Scala
  * Erlang

>   1. 変数のミュータブル・イミュータブルを採用した言語で（イメージとしての）上記のようなスケッチを実行させると、どのような結果が出るのでしょうか？
>

ScalaとErlangではそのような裏技的なコードを見たことがありません。言語仕様上、そういうコードを作るのは無理だと思います。Rustでは`unsafe`なコードを書くことで可能になります。

  * Rust：`unsafe`なコードならコンパイルも実行も可能。Rustはシステムプログラミングを可能にするために、安全でないコードもサポートしている。
  * Scala：そういうコードは書けない。イミュータブルな変数を書き換えようとするとコンパイルエラーになる。
  * Erlang：Erlangでは`=`はRustやScalaと少し違う意味を持つので`x = 2`に相当する式は合法であり、コンパイルできる。しかし実行時に`x`の値（つまり`1`）と`2`をパターンマッチできないというエラーになる。開発者はその実行時エラーによって、イミュータブルな変数を書き換えようとしていたことに気づく。

Rustの場合、たとえば以下のように書けます。

[code]

    fn main() {
        let x = 1;  // i32型
        println!("変数の値は{}", x);
    
        // xが格納されているアドレスを指す参照（ポインタ）を取得する
        let x_ref = &x;  // &i32型
        unsafe {
            // 参照から、ミュータブルな生ポインタを強制的に作り出す
            // x_raw_ptrは*mut i32型
            let x_raw_ptr = std::mem::transmute::<&i32,  *mut i32>(x_ref);
            // ミュータブルな生ポインタが指す値（xの値）を変更する
            *x_raw_ptr = 2;
        }
    
        println!("新し変数の値は{}", x);
    }
    
[/code]

>   2.
> 変数のミュータブル・イミュータブルを採用したコンパイル言語で、上記のスケッチをコンパイルして、それが通った場合どのような不具合が出るのでしょうか（言語名も挙げてもらえると嬉しいです。）？
>

イミュータブルな変数がなんとかして書き換えられてしまった場合、どのような不具合が出るのか、あるいは全く不具合が出ないのかはそのコードしだいです。

たとえばよく似た名前のミュータブルな変数が2つあったとします。開発者が間違って書き換えたかったのとは違う変数の値を書き換えてしまったらどうなるでしょうか？
そのコードを実行すると、たぶん意図していたのとは違う振る舞いをするでしょう。でも、なにも悪いことが起こらないのもあり得ます。このようなプログラミングのミスは、もし間違えられてしまった方の変数をイミュータブルできる状況なら、コンパイラが見つけてくれます。

イミュータブルな変数を持つ言語ではそれを持たない言語に比べて、言語処理系がより多くのプログラミングのミスを見つけてくれます。プログラムの規模が大きくなるほど、ありがたみを感じる場面が増えてくるでしょう。


CC BY-SA 4.0
2018-11-24T04:32:08.763
50582
2018-11-24T04:32:08.763
14101
50558
answer