引数
当然1000は固定でなく、引数で指定したいですよね。そのようにコードを変更してみました。
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let n: i64 = args[1].parse().unwrap();
let mut s = 0;
for i in 1..n {
if i % 3 == 0 || i % 5 == 0 {
s += i;
}
}
println!("{}", s);
}
use std::env;
モジュールをインポートしているようです。std::envは引数を取るのに必要です。
let args: Vec<String> = env::args().collect();
引数を配列に格納しています。
env:argsは、引数のiteratorを返すんですね。それをcollectで配列にすると。
ここで、Vecは可変長配列で、その中身の方がStringというわけです。
エラー処理
let n: i64 = args[1].parse().unwrap();
parseで文字列を整数にするわけですが、もし整数にならなかった場合、Rustでは例外を投げるのではなく、Errを返します。
より正確には、unwrapはここではResult<i64,E>を返します。Result<T>は、Err<E>か、Ok<T>のどちらか取ります。Haskellのようにマッチングが使えて、
let n: i64;
match args[1].parse() {
Ok(val) => n = val,
Err(err) => panic!("error : {}", err),
}
ここで、整数にならない文字列を引数にすると、
$ ./e001a 1000.
thread 'main' panicked at 'error : invalid digit found in string', e001a.rs:8:15
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
と、Err(err)にマッチしてそれ以降を処理するわけです。
ただ、たかがProject Eulerの問題を解くためにこんなエラー処理をしているわけにいかないので、Ok(val)であると仮定して、unwrapでひっぺ返して、valにするわけです。最初のコードだと、
$ ./e001a 1000.
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
よく分からないところでエラーになってしまいます。
速度
さて、ここでC++と速度を比較してみましょう。C++で同様に書いて、
#include <iostream>
using namespace std;
int main(int argc, char **argv) {
const int n = atoi(argv[1]);
long long s = 0;
for(int i = 1; i < n; ++i) {
if(i%3 == 0 || i%5 == 0) {
s += i;
}
}
cout << s << endl;
}
$ g++ -O2 e001.cpp
$ time ./a.out 1000000000
233333333166666668
real 0m1.133s
user 0m1.125s
sys 0m0.000s
Rustも最適化オプションをつけて、
$ rustc -O e001a.rs
$ time ./e001a 1000000000
233333333166666668
real 0m1.221s
user 0m1.203s
sys 0m0.000s
差は確かにあるようですね。