なぜSwiftは静的な型を採用したのか?
前回紹介したとおり、
- 安全
(Safe) - 高速
(Fast) - 豊かな表現力
(Expressive)
という3つの特長が挙げられています。ところでSwiftは静的型をしています。つまりSwiftの生みの親たちは静的型がこの3つの特長に有利だと判断したということですが、
varでも変えられないもの
では早速実例を見てみましょう。PlaygroundかREPLで、
var number = 20
number += 1
number *= 2
number += 0.195
最後のところでerror: binary operator'+=' cannot be applied to operands of type 'Int' and 'Double'
というエラーが出ているはずです。整数に浮動小数点数は足してはダメだということです。
今度はRubyで同じことをしてみましょう。以下はirbで実行してみた例です。
irb(main):001:0> number = 20 => 20 irb(main):002:0> number += 1 => 21 irb(main):003:0> number *= 2 => 42 irb(main):004:0> number += 0.195 => 42.195
一見Rubyの方が便利に思えます。が、
irb(main):005:0> number = "#{number}km" => "42.195km" irb(main):006:0> number *= 2 => "42.195km42.195km"
いつの間に数値だったnumber
は文字列になってしまっています。
今度はSwiftに戻って次のようにしてみましょう。
var number:Double = 20
number += 1
number *= 2
number += 0.195
今度は42.
number = "\(number)km"
としてみると、error: cannot assign value of type 'String' to type 'Double'
で止まります。
この観察から、
- 型は変数宣言の時点で決まる
- 型を明示しなくともよい。しない場合は推論される
- 変数に異なる型の値を代入することはできない
1つ注意が必要なのは、
$ cat > ./helloswift #!/usr/ bin/ env swift print("Hello, World!") $ chmod +x ./ helloswift $ ./ helloswift Hello, World!
にもかかわらずSwiftが静的型を採用しているのは、
型ははめてなんぼ
それでは型があった方が本当に安全なのか。やはり実例で見てみましょう。ここでは標準入力に行番号をつけて出力するだけの簡単なお仕事をしてみます。コマンドであればcat -n
、perl -e 'print"$.:$_"while<>'
ですが、
var count = 0
while let line = readLine() {
count += 1
print("\(count): \(line)")
}
簡単ですね。ではCは?
#include <stdio.h>
int main() {
char line[64];
int count = 0;
while (gets(line)) {
printf("%d: %s\n", ++count, line);
}
}
そんなに難しそうには見えませんね。#include<stdio.
とかint main(){}
とか余計なおまじないが入っていたり、char line[64]
とかintcount
とか変数の型を明示している点を除けばSwiftとさほど違いはないように見えます。
本当に
$ cc -Wall cat-n.c $ ruby -e 'puts "1"*42' ¦ ./ a. out warning: this program uses gets(), which is unsafe. 1: 111111111111111111111111111111111111111111
警告は出ていますが、
$ ruby -e 'puts "1"*4096' ¦ ./a. out warning: this program uses gets(), which is unsafe. Segmentation fault
落ちちゃいました。何が問題だったのでしょう? ソースを見てみると、line
はchar
64個からなる配列として定義されています。なのに4,096文字もある行を喰わせたらバッファーオバーフローするのは当然ですね。文字列ではないのです。文字の配列です。じゃあ文字列を指定すればいい? どこにあるんですかCの文字列なんて!
というわけでgets()
ではなくfgets()
を使って書き直して見ましょう。
#include <stdio.h>
#define CHARSPERLINE 64
int main() {
char line[CHARSPERLINE];
int count = 0;
while (fgets(line, CHARSPERLINE, stdin)) {
printf("%d: %s\n", ++count, line);
}
}
$ ruby -e 'puts "1"*4096' ¦ ./a. out 1: 111111111111111111111111111111111111111111111111111111111111111 …… (中略) …… 65: 111111111111111111111111111111111111111111111111111111111111111 66: 1
確かにSegmentation faultは出なくなりましたが、CHARSPERLINE
が64と小さいのが原因ですが、
4096? 65536? そもそもそれってプログラマが頭を悩ませるべきことでしょうか?
気を取り直して、
$ swiftc cat-n.swift $ ruby -e 'puts "1"*4096' ¦ ./ cat-n 1: 1111… …… (中略) …… 1111111111111111111
今度はきちんと1行におさまりました。
Cの問題は一体なんだったのでしょう? 「文字列」
今となってはCという言語は静的型と動的型の悪いところ取り言語にどうしても見えてしまいます。Cで
そもそも型はなんのためにあるのか。安全で高速なプログラムを実現するためではないのですか? 余計なコードを実行せず、
もちろん型は安全性や高速性の特効薬ではありません。Swift自体、
文字列という基本的な型一つ見ても、
本誌最新号をチェック!
Software Design 2022年9月号
2022年8月18日発売
B5判/
定価1,342円
- 第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識 - 第2特集
「知りたい」 「使いたい」 「発信したい」 をかなえる
OSSソースコードリーディングのススメ - 特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画] Red Hat Enterprise Linux 9最新ガイド - 短期連載
今さら聞けないSSH
[前編] リモートログインとコマンドの実行 - 短期連載
MySQLで学ぶ文字コード
[最終回] 文字コードのハマりどころTips集 - 短期連載
新生「Ansible」 徹底解説
[4] Playbookの実行環境 (基礎編)