5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-09-09

転職のための勉強日記です: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も静的型付言語ですが基本的型推論が効きます.

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は似ている:bangbang:7

注釈

  1. FlutterではUIツールキットと呼んでいます.SwiftUIはフレームワークとドキュメントには書いてありましたのでそちらに合わせました.

  2. SwiftUI won't register code elements in the editor

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

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

  5. Structures and Classes 2

  6. 名前はRustのCloneトレイトとDebugトレイトを意識した.

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

5
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?