Swift まったく書いたことない僕ですが、最近会社で余暇 (?) 先輩に iOS 開発学習を赤ちゃん用の浅いビニールプールでぱちゃぱちゃ泳がせてもらうかんじで初学者としてたのしく学んでいる。
そのときに教えてもらってユニークでおもしろかったのをメモしておいたので、記念にかいておこうとおもうよ〜〜数年後恥ずかしくなって削除できてると成長が実感できるというしくみになっている。そもそもこの記事の理解も死ぬほど曲解しててすでに大変なことになってるかもしれないけどそういう💣爆弾をたのしんでいきましょう人生🤪
KeyPath
Swift でまずびっくりしたのが KeyPath ってやつ。
プロパティまでの参照を定義して適用できるのおもしろ! という感じ。ユニーク。使ってみたくなるふしぎな魅力がありますな。class
でも KeyPath が使えるのが面白い。
struct MyStruct { var a: Int var b: Int } var ms = MyStruct(a: 10, b: 20) let msaPath = \MyStruct.a let msbPath = \MyStruct.b print("ms.a: \(ms[keyPath: msaPath])") print("ms.b: \(ms[keyPath: msbPath])") ms[keyPath: msaPath] = 111 ms[keyPath: msbPath] = 222 print("ms.a: \(ms.a)") print("ms.b: \(ms.b)")
↑ でもこの例は KeyPath 1 ミリもわかってないやつの例というかんじがしておそれいりますね……🫢
C の構造体のベースのアドレスからポインタをサイズ分移動させたらメンバーを参照できたりする (パディングを考えなければ) やつをおもいだした:
#include <stdio.h> #include <stdint.h> #include <inttypes.h> typedef struct MyStruct { uint64_t a; uint64_t b; } __attribute__((packed)) MyStruct; int main(void) { MyStruct ms; MyStruct *pms = &ms; ms.a = 10; ms.b = 20; uint64_t *msa = ((void *)pms); uint64_t *msb = ((void *)pms + sizeof(uint64_t)); printf("msa: %" PRIu64 "\n", *msa); printf("msb: %" PRIu64 "\n", *msb); *msa = 111; *msb = 222; printf("ms.a: %" PRIu64 "\n", ms.a); printf("ms.b: %" PRIu64 "\n", ms.b); }
msa: 10 msb: 20 ms.a: 111 ms.b: 222
使いどころきいた記憶があるのだけど忘れてしまってなんだっけ……となっているけど、いろいろありそうであった:
引数のラベル
つけられるのはよくあるとして、つけないとダメなのとつけるとよくないときがあっておもしろい:
func f(_ a: Int, _ b: Int, c: String) { print("a = \(a), b = \(b), c = \(c)") } f(1, 2, c: "string") // f(1, b: 2, c: "string") とすると怒られる // f(1, 2, "string") としても怒られる)
Kotlin だとつけちゃダメみたいなのがないので、 つけたら怒られるのがおもしろい。絶対なにか理由があると思う。Python にもこういうのがありますな。
使い分け的などういう慣習でつけたりつけてなかったりするのかはわかってない。
resultBuilder
ブロックを書いて中で構築したりするのにいい感じの仕組みだと認識してるけどむずかしいらしい。
Kotlin だと trailing closure を駆使してビルダーを書いたりするわけだけど そういう感じのもの?
とも思ったんだけど、Swift でも trailing closure ってあるから、なにかもっと便利なのでしょうな
guard
これを先頭で使ってガード節を書くと Swift っぽいとのこと:
func f(_ word: String) { guard !word.isEmpty else { return } print("word = \(word)") } f("hello") f("") f("friend")
きっとやっていくとその理由がわかるのだと思う。
struct
, mutating func
これ Kotlin で考えたら let
でいいじゃんとおもったらダメなやつがあって、なんで? と思ったら struct
と mutating func
とかの兼ね合いでムリというような事情があるようだった。
struct S { var a: Int mutating func mutate() { a += 10 } } class K { var a: Int init(a: Int) { self.a = a } func mutate() { a += 10 } } var s = S(a: 10) // let だとだめ s.mutate() print("s.a = \(s.a)") let k = K(a: 10) k.mutate() print("k.a = \(k.a)")
なんで class
だといいんだろ……。なにか理由があるんでしょうな。
C++ の const
にちょっと通じるものがあるなとおもった。struct
と class
があるのもにてるかんじがありますよね。C++ の struct
と class
ってデフォルトのアクセス指定子 が private
か public
かとかの違いがあるくらいだったという記憶がある。
#include <iostream> class Klass { public: void fn1() const { std::cout << this << ": fn1" << std::endl; } void fn2() { std::cout << this << ": fn2" << std::endl; } }; int main(void){ Klass k1; const Klass k2; k1.fn1(); k1.fn2(); k2.fn1(); k2.fn2(); // これはコンパイルエラー }
Main.cpp:22:5: error: 'this' argument to member function 'fn2' has type 'const Klass', but function is not marked const k2.fn2(); // これはコンパイルエラー ^~ Main.cpp:9:10: note: 'fn2' declared here void fn2() { ^ 1 error generated.
言語の入門は異文化交流感がありつつ知った言語との似通った部分があって面白いので、ドライバーがまったく知らない言語でペアプロしたりすると面白いかもしれないですね。みなさん僕にあなたの好きな言語をおしえてみませんか。