[Rust]Rustlingsで学ぼう(Variables)

Rust

Rustlingsとは

RustlingsはRustコードに慣れるための演習が詰まったパッケージです。
概要その他については以下を参照してください。

今回の演習

今回はVariables、つまり変数ですね。
The Bookでは「3.1. Variables and Mutability(3.1. 変数と可変数)」になり、変数、定数、シャドーイングが対象となるようです。(ここでシャドーイングとは変数の再宣言のことです。なぜ、シャドウ?というのは再宣言することで元の変数がシャドウ(隠れる)ためです。)

variables

それではvariablesの6問です。

variables1

出力内容は以下です。

⚠️  Compiling of exercises/variables/variables1.rs failed! Please try again. Here's the output:
error[E0425]: cannot find value `x` in this scope
  --> exercises/variables/variables1.rs:11:5
   |
11 |     x = 5;
   |     ^
   |
help: you might have meant to introduce a new binding
   |
11 |     let x = 5;
   |     +++

error[E0425]: cannot find value `x` in this scope
  --> exercises/variables/variables1.rs:12:36
   |
12 |     println!("x has the value {}", x);
   |                                    ^ not found in this scope

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0425`.

変数の宣言にはletキーワードが必要になります。ない場合は代入となりますが、スコープ内にxがまだ宣言されていない状態では使用できないため、上記エラーが発生します。

なので親切なエラーメッセージの通り、letキーワードを付け加えましょう。

variables2

次はこのように出力されます。

⚠️  Compiling of exercises/variables/variables2.rs failed! Please try again. Here's the output:
error[E0283]: type annotations needed
  --> exercises/variables/variables2.rs:9:9
   |
9  |     let x;
   |         ^
10 |     if x == 10 {
   |          -- type must be known at this point
   |
   = note: cannot satisfy `_: PartialEq<i32>`
help: consider giving `x` an explicit type
   |
9  |     let x: /* Type */;
   |          ++++++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0283`.

こちらは親切なエラーメッセージによってわかりにくくなった感じですかね?

メッセージではxの型がわからないためx == 10を評価できないと言っています。なのでlet x: の後に型注釈(annotate)してねと言っているので型注釈してみましょう。例えば

let x: i32;

のようにすると、次は以下のエラーメッセージが表示されます。

⚠️  Compiling of exercises/variables/variables2.rs failed! Please try again. Here's the output:
error[E0381]: used binding `x` isn't initialized
  --> exercises/variables/variables2.rs:10:8
   |
9  |     let x: i32;
   |         - binding declared here but left uninitialized
10 |     if x == 10 {
   |        ^ `x` used here but it isn't initialized
   |
help: consider assigning a value
   |
9  |     let x: i32 = 0;
   |                +++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0381`.

次はxが初期化されていないのでまたまたx == 10を評価できないと言っています。なのでエラーメッセージの案内通り初期化してみましょう。

let x: i32 = 10;

そうするとこの演習はクリアです。

ですが、Rustは高性能な型推測が可能です。そのため、最初から以下のように記述することでこの演習はクリアできます。

let x = 10;

でも型を意識するのはとても大事です。

variables3

⚠️  Compiling of exercises/variables/variables3.rs failed! Please try again. Here's the output:
error[E0381]: used binding `x` isn't initialized
  --> exercises/variables/variables3.rs:10:27
   |
9  |     let x: i32;
   |         - binding declared here but left uninitialized
10 |     println!("Number {}", x);
   |                           ^ `x` used here but it isn't initialized
   |
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider assigning a value
   |
9  |     let x: i32 = 0;
   |                +++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0381`.

こちらは間に書かれた注記(note)を除けばとてもわかりやすいメッセージです。xという変数が初期化されていないですよ、ってことです。なので初期化しましょう。

let x: i32 = 0;

ちなみに注記部分はprintlnマクロのどの部分でエラーとなっているよ、というのを教えてくれていますね。

variables4

⚠️  Compiling of exercises/variables/variables4.rs failed! Please try again. Here's the output:
error[E0384]: cannot assign twice to immutable variable `x`
  --> exercises/variables/variables4.rs:11:5
   |
9  |     let x = 3;
   |         -
   |         |
   |         first assignment to `x`
   |         help: consider making this binding mutable: `mut x`
10 |     println!("Number {}", x);
11 |     x = 5; // don't change this line
   |     ^^^^^ cannot assign twice to immutable variable

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0384`.

Rustでは、デフォルトで変数は不変(immutable)です。そのため、x = 5;で二回目の代入はできないのです。

もし、同じ変数に代入したい場合は2つの方法があります。1つ目はメッセージにある通り、可変(mutable)な変数宣言にするというものです。

let mut x = 3;

このように宣言された変数は可変となり、後のコードで値を変更できます。

もう一つの方法はシャドーイングです。せっかくなのでシャドーイングについては次の演習で利用してみようと思います。

variables5

⚠️  Compiling of exercises/variables/variables5.rs failed! Please try again. Here's the output:
error[E0308]: mismatched types
  --> exercises/variables/variables5.rs:11:14
   |
9  |     let number = "T-H-R-E-E"; // don't change this line
   |                  ----------- expected due to this value
10 |     println!("Spell a Number : {}", number);
11 |     number = 3; // don't rename this variable
   |              ^ expected `&str`, found integer

error[E0369]: cannot add `{integer}` to `&str`
  --> exercises/variables/variables5.rs:12:48
   |
12 |     println!("Number plus two is : {}", number + 2);
   |                                         ------ ^ - {integer}
   |                                         |
   |                                         &str

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0308, E0369.
For more information about an error, try `rustc --explain E0308`.

こちらもvariables4と同じく再代入の課題になっています。更にこちらは全く型が違う(&strからinteger)値の代入です。型が異なるため、variables4と同じような可変変数(mut)にすることでの解決は図れません。

そこでここではシャドーイングを使ってみます。シャドーイングとは変数の再宣言で、変数名は同じでも再宣言以降は以前の変数は利用することはできず(シャドウされる)、新しい変数宣言に従って処理が継続します。そのため、型や可変性の変更も可能です。

let number = "T-H-R-E-E";
let number = 3; // mutも付加できます

variables6

variablesの最後は定数宣言です。

  Compiling of exercises/variables/variables6.rs failed! Please try again. Here's the output:
error: missing type for `const` item
 --> exercises/variables/variables6.rs:8:13
  |
8 | const NUMBER = 3;
  |             ^ help: provide a type for the constant: `: i32`

error: aborting due to 1 previous error

エラーメッセージは少ないですね。Rustでは定数宣言にいくつかの決まりや特徴があります。

  • constキーワードを使って宣言します。
  • 型注釈が必須です。
  • グローバルスコープでの宣言も可能です。つまりプログラムの実行中は常に有効です。
  • 値は定数式のみ設定可能です。実行時にのみ計算できる値は使用できません。
  • Rustの命名規則では、すべて大文字を使用し、単語の間はアンダースコアを入れます。(※コンパイルエラーにはなりません。)

今回のエラーは上記の内、2番目のお作法に従っていないために発生したエラーとなります。

const Number: i32 = 3;

rustc --explain

最後にせっかくなので各エラーメッセージにも表示されていた各エラーコード?の説明文も見てみましょう。

E0425 -- variables1

An unresolved name was used.

Erroneous code examples:

something_that_doesnt_exist::foo;
// error: unresolved name `something_that_doesnt_exist::foo`

// or:

trait Foo {
    fn bar() {
        Self; // error: unresolved name `Self`
    }
}

// or:

let x = unknown_variable;  // error: unresolved name `unknown_variable`

Please verify that the name wasn't misspelled and ensure that the identifier being referred to is valid for the given situation. Example:

enum something_that_does_exist {
    Foo,
}

Or:

mod something_that_does_exist {
    pub static foo : i32 = 0i32;
}

something_that_does_exist::foo; // ok!

Or:

let unknown_variable = 12u32;
let x = unknown_variable; // ok!

If the item is not defined in the current module, it must be imported using a use statement, like so:

use foo::bar;
bar();

If the item you are importing is not defined in some super-module of the current module, then it must also be declared as public (e.g., pub
fn).

こちらのエラーメッセージは「解決できない名前が使われている」として3つのエラーが定義されています。

something_that_doesnt_exist::foo;
trait Foo {
    fn bar() {
        Self; // error: unresolved name `Self`
    }
}
let x = unknown_variable;

これに対して

  • スペルミスではないですか?
  • 有効な名前(識別子)が有効な場所で使用されていますか?
  • 対象のモジュールで名前(識別子)が宣言されていなければ、useステートメントを使ってモジュールをインポートしましょう。
  • インポートする名前(識別子)が参照可能であるかどうかも確認してね。(public宣言など)

というヘルプが記述されています。variable1では変数宣言自体が間違っているんですけどね。

E0381 -- variables2, variables3

It is not allowed to use or capture an uninitialized variable.

Erroneous code example:

fn main() {
    let x: i32;
    let y = x; // error, use of possibly-uninitialized variable
}

To fix this, ensure that any declared variables are initialized before being used. Example:

fn main() {
    let x: i32 = 0;
    let y = x; // ok!
}

「初期化する前に使うな」と言っています。

E0384 -- variables4

An immutable variable was reassigned.

Erroneous code example:

fn main() {
    let x = 3;
    x = 5; // error, reassignment of immutable variable
}

By default, variables in Rust are immutable. To fix this error, add the keyword mut after the keyword let when declaring the variable. For
example:

fn main() {
    let mut x = 3;
    x = 5;
}

「不変変数には再代入できない」と言っています。なので可変変数にするにはmutキーワードつけてね、と。

E0308 -- variables5

Expected type did not match the received type.

Erroneous code examples:

fn plus_one(x: i32) -> i32 {
    x + 1
}

plus_one("Not a number");
//       ^^^^^^^^^^^^^^ expected `i32`, found `&str`

if "Not a bool" {
// ^^^^^^^^^^^^ expected `bool`, found `&str`
}

let x: f32 = "Not a float";
//     ---   ^^^^^^^^^^^^^ expected `f32`, found `&str`
//     |
//     expected due to this

This error occurs when an expression was used in a place where the compiler expected an expression of a different type. It can occur in
several cases, the most common being when calling a function and passing an argument which has a different type than the matching type in
the function declaration.

「代入しようとしている値の型が違う」って言っています。関数呼び出しでよく見ますね。

E0369 -- variables5

A binary operation was attempted on a type which doesn't support it.

Erroneous code example:

let x = 12f32; // error: binary operation `<<` cannot be applied to
               //        type `f32`

x << 2;

To fix this error, please check that this type implements this binary operation. Example:

let x = 12u32; // the `u32` type does implement it:
               // https://doc.rust-lang.org/stable/std/ops/trait.Shl.html

x << 2; // ok!

It is also possible to overload most operators for your own type by implementing traits from std::ops.

String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the
string on the left. If something should be added to a string literal, move the literal to the heap by allocating it with to_owned() like in "Your
text".to_owned().

最初の説明文は「サポートされていない型でバイナリ操作しようとしています」なんですけど、むしろ最後にあるメッセージの通り、文字列リテラルとStringの連結でよく見るメッセージです。所有権の考え方も相まって最初に混乱をきたすところかも。

最後に

だいぶ間が開いてしまいました。

タイトルとURLをコピーしました