転職のための勉強日記です. 世の中を簡単にをモットーに頑張っていきたいと思います.
概要
FlutterとかSwiftUIとか楽しそうなフレームワーク1が出てきました. SwiftUIをとりあえず勉強しようと思ったのですが現状ベータでプレビューが上手く表示されない2などの不具合に見舞われたので正式リリースまでは言語の方を勉強しようと思います. Swiftは出始めに少し触ったことがあるのですが, Dartは初めてです. そのためSwiftを基本としつつ, 対応するような機能がDartにあるのかという形式で書きたいと思います.3 両者が似ていれば勉強も楽というものです.
バージョン
以下のバージョンでの比較です.
Swift | Dart |
---|---|
5.0.1 | 2.4.1 |
比較
機能 | Swift | Dart |
---|---|---|
型推論 | ✅ | ✅ |
定数 | let | const/final |
クラス | ✅ | ✅ |
継承 | ✅ | ✅ |
継承 | ✅ | ✅ |
列挙型 | ✅ | ✅ |
無名関数 | ✅ | ✅ |
ジェネリックス型 | ✅ | ✅ |
ジェネリックス型関数 | ✅ | ✅ |
ジェネリックス型制約 | ✅ | ✅ |
プロトコル/インタフェース | ✅ | ✅ |
プロトコル拡張/ミックスイン | ✅ | ✅ |
オプション型 | ✅ | ❌ |
構造体 | ✅ | ❌ |
多重継承 | ❌ | ✅ |
不透明型 | ❌ | ✅ |
変数
SwiftもDartも静的型付言語ですが基本的型推論が効きます.
var str = "I'm Swift!"
var str = "I'm Dart!";
定数
定数はletとconstの違いがあります.
let name = "BrainVader"
const name = "BrainVader";
変数補完
文字列中に変数を埋め込める地味に助かる構文です. Rustには無かったりして辛いですが, SwiftとDartにはあります.
let name = "BrainVader"
let greeting = "Hello \(name)!"
const name = "BrainVader";
final greeting = "Hello ${name}!";
型注釈と明示型
基本的にどちらの言語も型推論で済ますことができると思うのですが, 型を明示することもできます.
let name: String = "BrainVader"
const string NAME = "BrainVader";
for
基本的な制御構文は変わりませんが, Swiftには単純なforループはありません.
var count = 0
for _ in 0..<100 {
count += 1;
}
var count = 0;
for(var i = 0; i < 100; i++) {
count++;
}
print("count ${count}");
let GAFA = ["Google", "Amazon", "Facebook", "Apple"]
print("GAFA stands for the following companies...")
for name in names {
print("\(name)!")
}
const GAFA = [0, 1, 2];
for (var x in collection) {
print(x); // 0 1 2
}
while
var count = 0
let end = 10
while !(count == end) {
count += 1;
}
var count = 0;
const END= 10;
while (count != END) {
count++;
}
print('Total ${count}');
if
ifはほぼ同じです.
let LANGUAGE = "Swift"
if LANGUAGE == "Dart" {
print("Dart");
} else {
print("Swift");
}
const LANGUAGE = "Dart";
if ( LANGUAGE == "Dart") {
print("Dart");
} else {
print("Swift");
}
switch文
基本的なswitch文も小さな違いがあるだけです.
let language = "Swift"
switch language {
case "Swift":
print("This is Swift")
default:
print("This is other language")
}
const language = "Dart";
switch (language) {
case "Swift":
print("This is Swift");
break;
default:
print("This is other language");
break;
}
関数
関数は返り値を後ろに置くか前に置くかが違います.
func greet(name: String) -> String {
return "Hello, \(name)!"
}
let message = greet(name: "Dart")
String greet(String name) {
return "Hello, ${name}!";
}
final message = greet("Swift");
print(message);
Swiftでは呼び出し時に引数ラベル4を指定するのがデフォルトですが, 名前付きパラメータで同じことができます.
String greet({String name}) {
return "Hello, ${name}!";
}
var message = greet(name: "Swift");
print(message);
Swiftでも引数ラベルを省略したい場合は以下のようになります.
func greet(_ name: String) -> String {
return "Hello, \(name)!"
}
クロージャ/無名関数
厳密には違うものとは思いますが使われ方はほぼ同じと思います.
let nums = [1, 2, 3, 4];
let multiplyBy2 = {(num) -> Int in num * 2}
let result = nums.map(multiplyBy2);
print(nums);
print(result);
const nums = [1, 2, 3, 4];
var multiplyBy2 = (num) => num * 2;
var result = nums.map(multiplyBy2).toList();
print(nums);
print(result);
この比較だとSwiftの記法が少し不自然に見えます.5 しかしこれは末尾クロージャという記法を使うとより自然に見えます.
let nums = [1, 2, 3, 4]
let result = nums
.filter {(num: Int) -> Bool in num % 2 == 0 }
.map {(num: Int) -> Int in num * 2}
print(nums);
print(result);
クロージャでも型推論が効きますので型は省略できます. 更にコマンドライン引数のようにドルマーク(💲)と番号でアクセスできます. ここまでやるとSwiftの方が読みやすい気もします.
let nums = [1, 2, 3, 4]
let result = nums.filter {$0 % 2 == 0}.map {$0 * 2}
print(nums);
print(result);
列挙型
列挙型も基本は同じです.
enum Language {
case Swift
case Dart
case Python
case Rust
}
let myLanguage = Language.Swift
let yourLanguage = {print($0)}
switch myLanguage {
case .Swift:
yourLanguage("Swift")
case .Dart:
yourLanguage("Dart")
case .Python:
yourLanguage("Python")
case .Rust:
yourLanguage("Rust")
default:
print("No Language")
}
// Define in the global scope
enum Language {
Swift, Dart, Python, Rust
}
const myLanguage = Language.Dart;
final yourLanguage = (language) => {print(language)};
switch (myLanguage) {
case Language.Swift:
yourLanguage("Swift");
break;
case Language.Dart:
yourLanguage("Dart");
break;
case Language.Python:
yourLanguage("Python");
break;
case Language.Rust:
yourLanguage("Rust");
break;
default:
print("No Language");
break;
}
Swiftの列挙型には関連値(associated value)が指定できます.
enum MouseState {
case Start(Int, Int)
case Move(Int, Int, Int, Int)
case End(Int, Int)
}
let dragging = MouseState.Move(0, 0, 100, 100)
switch dragging {
case MouseState.Start(let x, let y):
print("Start at (\(x), \(y))")
case MouseState.Move(let x_0, let y_0, let x, let y):
print("Dragging from (\(x_0), \(y_0)) to (\(x), \(y))")
case MouseState.End(let x, let y):
print("End at (\(x), \(y))")
}
構造体
Dartには無いようですがSwiftでは構造体の利用が基本なようなので書いておきます.
As a general guideline, prefer structures because they’re easier to reason about, and use classes when they’re appropriate or necessary.5
C言語の構造体とは違いメソッドを持てます.
struct Neko {
let age: Int;
let kind: String;
let sex: String;
func say() -> String {
return "nyao!"
}
}
func say(neko: Neko) -> () {
print(neko.say());
}
let neko = Neko(age: 5, kind: "Calico", sex: "Male");
say(neko: neko);
クラス
クラスは他の言語と書き方は同じです. クラス・メソッド, インスタンス・メソッド, コンストラクタなどが定義できます.
class Neko {
let age: Int;
let kind: String;
let sex: String;
static func clone(from neko: Neko) -> Neko {
return Neko(age: neko.age, kind: neko.kind, sex: neko.sex)
}
static func info(about neko: Neko) -> String {
return "age: \(neko.age), kind: \(neko.kind), sex: \(neko.sex)";
}
init(age: Int, kind: String, sex: String) {
self.age = age
self.kind = kind
self.sex = sex
}
func getOld() -> Neko {
return Neko(age: self.age + 1, kind: self.kind, sex: self.sex)
}
}
class Neko {
final num age;
final String kind;
final String sex;
static Neko clone({Neko neko}) {
return Neko(neko.age, neko.kind, neko.sex);
}
static String info({Neko neko}) {
return "age: ${neko.age}, kind: ${neko.kind}, sex: ${neko.sex}";
}
Neko(this.age, this.kind, this.sex);
}
プロトコル/インターフェース/Mixins
いわゆる実装を持たない振る舞いだけを定義したもののことです.
protocol Animal {
var age: Int { get };
var kind: String { get };
var sex: String { get };
func getOld() -> Animal;
}
protocol Clone {
func clone() -> Any;
}
protocol Debug {
func info() -> String;
}
class Neko: Animal, Clone, Debug {
// define class body here!
}
Dartで静的メソッドを継承させることができないのとmixinを使った方が良いのではということでmixinにしてみた. こちらの方がインターフェースに近い気がする.6
mixin Animal {
num age;
String kind;
String sex;
Animal getOld();
}
mixin Clone<T> {
T clone();
}
mixin Debug<T> {
String info();
}
class Neko with Animal, Clone, Debug {
// define class body here!
}
結論
SwiftとDartは似ている7