RustとRubyの違い
Mut
- Ruby: 変数は値変えられます
hoge = 1
hoge = 2
p hoge
# 結果: => 2
- Rust: 変数は値変更不可(
immutable
)
fn main() {
let hoge = 1;
hoge = 2;
<!--
ビルドエラー
error[E0384]: cannot assign twice to immutable variable `hoge`
-->
println!("Hoge {:}", hoge);
}
- Rust: 変数の値変更したい場合、
mut
を付けないといけません
fn main() {
let mut hoge = 1;
hoge = 2;
println!("Hoge {:}", hoge);
<!-- 結果: Hoge 2 -->
}
型
- Ruby: 変数の型がありません、コンパイラーは値から型を付けます (PHPなどと同じ)
hoge = "as"
a.class
# => String
hoge = 12
hoge.class
# => Integer
- Rust: 変数の型は変更出来ません、(C, Java, Cotlin, Swiftなどと同じ)
fn main() {
let mut hoge = 1;
<!-- Hoge変数は `&str` 型になります-->
hoge = "hola";
// ビルドエラー
// error[E0308]: mismatched types
// |
// | hoge = "hola";
// | ^^^^^^ expected integer, found `&str`
println!("Hoge {:}", hoge);
}
変数のReference
- Rustはreferenceがあります。(Cと同じ)
fn main() {
let a: u8 = 10;
let b = &a;
println!("Pointer a: {:p}", b);
println!("Value a: {}", a);
println!("Value b: {}", b);
}
// Pointer a: 0x7ffd469b821f
// Value a: 10
// Value b: 10
}
- Ruby: 全部はobjectなので、変数はPointerです。Rustの書き方が存在しません
a = 12
b = &a
# コンパイラーエラー
# SyntaxError ((irb):23: syntax error, unexpected &)
- RubyのReference
class Hoge attr_accessor :name end
hoge = Hoge.new
fuga = hoge
hoge.name = "hoge"
p fuga.name
# 結果
# => "hoge"
fuga.name = "fuga"
p hoge.name
# 結果
# => "fuga"
質問1
fn main() {
let mut a: u8 = 10;
let b = &mut a;
a = 11;
println!("Value a: {:}", a);
}
→ エラーがありますか?
質問1の答え
- エラーはありません
質問2
fn main() {
let mut a: u8 = 10;
let b = &mut a;
a = 11;
println!("Value b: {:}", b);
}
→ 結果はどうなりますか?
質問2の答え
- エラー発生します
--> src/main.rs:7:3
|
7 | a = 11;
| ^
|
= note: `#[warn(unused_assignments)]` on by default
= help: maybe it is overwritten before being read?
error[E0506]: cannot assign to `a` because it is borrowed
--> src/main.rs:7:3
|
5 | let b = &mut a;
| ------ borrow of `a` occurs here
6 |
7 | a = 11;
| ^^^^^^ assignment to borrowed `a` occurs here
8 | println!("Value b: {:}", b);
| - borrow later used here
error: aborting due to previous error; 1 warning emitted
Borrowing と Ownership
- Rubyでオーナーとういう概念がない、複数変数はひとつのデータに参照するときに、どの変数でもデータ変更できます
class Hoge attr_accessor :name end
hoge = Hoge.new
fuga = hoge
hoge.name = "hoge"
p fuga.name
# 結果
# => "hoge"
fuga.name = "fuga"
p hoge.name
# 結果
# => "fuga"
- Rust: 一つの変数だけデータ変更できます。
struct Hoge {
count: i32,
}
fn main() {
let mut hoge = Hoge {count: 1};
let fuga = &mut hoge;
fuga.count = 2;
println!("Hoge count: {:}", hoge.count);
hoge.count = 3;
println!("Hoge count: {:}", fuga.count);
// error[E0502]: cannot borrow `hoge.count` as immutable because it is also borrowed as mutable
// --> src/main.rs:8:33
// |
// 6 | let fuga = &mut hoge;
// | --------- mutable borrow occurs here
// 7 | fuga.count = 2;
// 8 | println!("Hoge count: {:}", hoge.count);
// | ^^^^^^^^^^ immutable borrow occurs here
// 9 | hoge.count = 3;
// 10 | println!("Hoge count: {:}", fuga.count);
// | ---------- mutable borrow later used here
// error[E0506]: cannot assign to `hoge.count` because it is borrowed
// --> src/main.rs:9:5
// |
// 6 | let fuga = &mut hoge;
// | --------- borrow of `hoge.count` occurs here
// ...
// 9 | hoge.count = 3;
// | ^^^^^^^^^^^^^^ assignment to borrowed `hoge.count` occurs here
// 10 | println!("Hoge count: {:}", fuga.count);
// | ---------- borrow later used here
}
Nil vs Option
- Ruby: どの変数でもnil付けられます
x = 2;
y = nil;
print "x: #{x}, y: #{y}"
# 結果
# x: 2, y: => nil
- Rust: nilは存在しません → 代わりにデータがある・ないの型があります → Option
fn main() {
let x: Option<u32> = Some(2);
let y: Option<u32> = None;
println!("x: {:?}, y: {:?}", x, y)
}
- Rust OptionのNone = Rubyのnil
- RUst OptionのSome = RUbyのnilじゃない
- Ruby: Nilに関連するoperatorがありません
a = nil
b = a + 1
# NoMethodError (undefined method `+' for nil:NilClass)
- Rust: Optionに関連するoperatorもありません、ただOptionのデータで使うとときに、Noneをチェックしないといけません → 安全です
fn main() {
let a: Option<u32> = Some(2);
let value = match a {
Some(v) => v,
None => 0
};
let b = value + 1;
println!("b: {:}", b);
}
Result - エラーハンドリング
- エラーは外部APIまたは特別な処理の時に、エラー発生します。
例: FbApiError, ValidationError..
- Ruby: 関数開発の時にエラー気にしなくも開発出来ます
def call_api(endpoint, method)
fb_client.call(endpoint, method) # エラーがある処理
'API Done'
end
- Ruby: エラーハンドリングの時に開発者に決められます。
def call_api(endpoint, method)
begin
fb_client.call(endpoint, method)
rescue => exception
'API ERROR'
end
'API Done'
end
- Ruby: エラーハンドリングしないと関数が呼ばれるところにエラー投げます。
def call_api(endpoint, method)
fb_client.call(endpoint, method) # エラーがある処理
'API Done'
end
call_api("me", "GET") # fb_client.call でエラー発生したら、ここに止まります。
- Rust: 関数開発の時にエラーがあれば、Resultとうい型になります。
pub async fn call_api(&self, endpoint: &str, method: &str) -> String{
self.call(endpoint, method) // エラーがある処理
"API DONE".to_owned()
}
- Rust: Resultをみるとエラーがあるかどうか分かります。
pub async fn call_api(&self, endpoint: &str, method: &str) -> String{
return match self.call(endpoint, method) {
Ok(value) => "API DONE".to_owned(),
Err(error) => "API ERROR".to_owned()
}
}
- Rust: エラーハンドリングしないときに、エラー発生したら何も起こらない
pub async fn call_api(&self, endpoint: &str, method: &str) -> String{
await self.call(endpoint, method) // エラーがある処理
"API DONE".to_owned()
}
pub async fn run(&self){
await self.call_api("me", "GET") // .call でエラー発生しても、何も起こらない
}
まとめ
- Mut
- 型
- Reference
- Nil/Option
- Result