転職黙示録 (1) SwiftとDartは似ているか?

転職のための勉強日記です:sun_with_face:. 世の中を簡単にをモットーに頑張っていきたいと思います.


## 概要
　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も静的型付言語ですが基本的型推論が効きます.

```swift
var str = "I'm Swift!"
```

```dart
var str = "I'm Dart!";
```

### 定数

　定数はletとconstの違いがあります.

```swift
let name = "BrainVader"
```

```dart
const name = "BrainVader";
```

## 変数補完

　文字列中に変数を埋め込める地味に助かる構文です. Rustには無かったりして辛いですが, SwiftとDartにはあります.

```swift
let name = "BrainVader"
let greeting = "Hello \(name)!"
```

```dart
const name = "BrainVader";
final greeting = "Hello ${name}!";
```

## 型注釈と明示型

　基本的にどちらの言語も型推論で済ますことができると思うのですが, 型を明示することもできます.

```swift
let name: String = "BrainVader"
```

```dart
const string NAME = "BrainVader";
```

## for

　基本的な制御構文は変わりませんが, Swiftには単純なforループはありません.

```swift
var count = 0

for _ in 0..<100 {
    count += 1;
}
```

```dart
var count = 0;
for(var i = 0; i < 100; i++) {
 count++;
}
print("count ${count}");
```

```swift
let GAFA = ["Google", "Amazon", "Facebook", "Apple"]
print("GAFA stands for the following companies...")
for name in names {
    print("\(name)!")
}
```

```dart
const GAFA = [0, 1, 2];
for (var x in collection) {
  print(x); // 0 1 2
}
```

## while

```swift
var count = 0
let end = 10

while !(count == end) {
    count += 1;
}
```

```dart
var count = 0;
const END= 10;

while (count != END) {
    count++;
}
print('Total ${count}');
```

## if

　ifはほぼ同じです.

```swift
let LANGUAGE = "Swift"
if LANGUAGE == "Dart" {
    print("Dart");
} else {
    print("Swift");
}
```

```dart
const LANGUAGE = "Dart";
if ( LANGUAGE == "Dart") {
  print("Dart");
} else {
  print("Swift");
}
```

## switch文

　基本的なswitch文も小さな違いがあるだけです.

```swift
let language = "Swift"
switch language {
case "Swift":
    print("This is Swift")
default:
    print("This is other language")
}
```

```dart
const language = "Dart";
switch (language) {
case "Swift":
    print("This is Swift");
    break;
default:
    print("This is other language");
    break;
}
```

## 関数

　関数は返り値を後ろに置くか前に置くかが違います. 

```swift
func greet(name: String) -> String {
    return "Hello, \(name)!"
}

let message = greet(name: "Dart")
```

```dart
String greet(String name) {
  return "Hello, ${name}!";
}
final message = greet("Swift");
print(message);
```

　Swiftでは呼び出し時に引数ラベル[^4]を指定するのがデフォルトですが, 名前付きパラメータで同じことができます.

```dart
String greet({String name}) {
  return "Hello, ${name}!";
}
var message = greet(name: "Swift");
print(message);
```

 Swiftでも引数ラベルを省略したい場合は以下のようになります.

```swift
func greet(_ name: String) -> String {
    return "Hello, \(name)!"
}
```

## クロージャ/無名関数

　厳密には違うものとは思いますが使われ方はほぼ同じと思います.

```swift
let nums = [1, 2, 3, 4];
let multiplyBy2 = {(num) -> Int in num * 2}
let result = nums.map(multiplyBy2);
print(nums);
print(result);
```

```dart
const nums = [1, 2, 3, 4];
var multiplyBy2 = (num) => num * 2;
var result = nums.map(multiplyBy2).toList();
print(nums);
print(result);
```

　この比較だとSwiftの記法が少し不自然に見えます.[^6] しかしこれは末尾クロージャという記法を使うとより自然に見えます.

```swift
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の方が読みやすい気もします.

```swift
let nums = [1, 2, 3, 4]
let result = nums.filter {$0 % 2 == 0}.map {$0 * 2}
print(nums);
print(result);

```

## 列挙型

　列挙型も基本は同じです.

```swift
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")
}
```

```dart
// 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)が指定できます.

```swift
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.[^6]

　C言語の構造体とは違いメソッドを持てます.

```swift
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);
```

## クラス

　クラスは他の言語と書き方は同じです. クラス・メソッド, インスタンス・メソッド, コンストラクタなどが定義できます.

```swift
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)
    }
}
```

```dart
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

　いわゆる実装を持たない振る舞いだけを定義したもののことです.

```swift
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にしてみた. こちらの方がインターフェースに近い気がする.[^7]

```dart
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は似ている:bangbang:[^8]

## 注釈
[^1]: FlutterではUIツールキットと呼んでいます.SwiftUIはフレームワークと[ドキュメント](https://developer.apple.com/documentation/swiftui)には書いてありましたのでそちらに合わせました.

[^2]: [SwiftUI won't register code elements in the editor](https://forums.developer.apple.com/thread/120960)

[^3]: あくまで言語機能の比較なので細かい差異など構文の説明は省きます.

[^4]: 引数の前に付くので引数ラベルと呼び, 関数定義の方ではパラメータ名と呼び分けているようです.

[^5]: 何言語ネイティブなのかとか好きな言語とか色々あるとは思いますので個人の感想です.

[^6]: [Structures and Classes](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html)

[^7]: 名前はRustのCloneトレイトとDebugトレイトを意識した.

[^8]: 主にSwift側に紹介していない機能が大量にある気もしますが気になる人はドキュメントと呼んでください.