1. ホーム
  2. スイフト

[解決済み】Swift(UI)の "some "キーワードとは?)

2022-05-29 09:36:27

質問

新しい SwiftUIチュートリアル は次のようなコードを持っています。

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

2行目の単語 some という単語が、キーワードであるかのように強調されています。

Swift 5.1 には some をキーワードとして持っていないようで、他の単語である some という単語は、通常型が行くところに行くので、そこで何をすることができるのかわかりません。Swiftの新しい未発表のバージョンがあるのでしょうか?私が知らない方法で型に使用されている関数なのでしょうか?

というキーワードは何を意味するのでしょうか? some は何をするのでしょうか?

どのように解決するのですか?

some View 不透明な結果タイプ で紹介されているように SE-0244 で導入され、Xcode 11 の Swift 5.1 で利用可能です。これは、"reverse" 一般的なプレースホルダーであると考えることができます。

呼び出し元によって満たされる通常のジェネリックプレースホルダーとは異なります。

protocol P {}
struct S1 : P {}
struct S2 : P {}

func foo<T : P>(_ x: T) {}
foo(S1()) // Caller chooses T == S1.
foo(S2()) // Caller chooses T == S2.

不透明な結果型は、暗黙の汎用プレースホルダーで 実装 と考えることができますので

func bar() -> some P {
  return S1() // Implementation chooses S1 for the opaque result.
}

をこのように表示します。

func bar() -> <Output : P> Output {
  return S1() // Implementation chooses Output == S1.
}

実際、この機能の最終的な目標は、より明示的な形でリバース・ジェネリックを可能にすることで、例えば -> <T : Collection> T where T.Element == Int . 詳細については、この投稿を参照してください。 .

ここから得られる主なことは、関数が返す some P を返すものは、特定の シングル に準拠した具体的な型である。 P . 関数内で異なる適合する型を返そうとすると、コンパイラーエラーが発生します。

// error: Function declares an opaque return type, but the return
// statements in its body do not have matching underlying types.
func bar(_ x: Int) -> some P {
  if x > 10 {
    return S1()
  } else {
    return S2()
  }
}

暗黙のジェネリックプレースホルダーは複数の型では満たすことができないため

を返す関数とは対照的です。 P を返す関数とは対照的で、これは ともに S1S2 を表すので、任意の P 適合する値です。

func baz(_ x: Int) -> P {
  if x > 10 {
    return S1()
  } else {
    return S2()
  }
}

さて、では不透明な結果型はどのような利点があるのでしょうか? -> some P はプロトコルの戻り値型よりも -> P ?


1. 不透明な結果型をPATで使用することができる

プロトコルの現在の主な制限は、PAT (関連付けられた型を持つプロトコル) が実際の型として使用できないことです。これはおそらく言語の将来のバージョンで解除される制限ですが、不透明な結果型は事実上単なる一般的なプレースホルダーなので、今日PATと一緒に使用することができます。

つまり、次のようなことができるのです。

func giveMeACollection() -> some Collection {
  return [1, 2, 3]
}

let collection = giveMeACollection()
print(collection.count) // 3


2. 不透明な結果型は同一性を持つ

不透明な結果型は単一の具象型を返すことを強制するため、コンパイラは同じ関数への2回の呼び出しが同じ型の2つの値を返さなければならないことを知ります。

これは、次のようなことができることを意味します。

//   foo() -> <Output : Equatable> Output {
func foo() -> some Equatable { 
  return 5 // The opaque result type is inferred to be Int.
}

let x = foo()
let y = foo()
print(x == y) // Legal both x and y have the return type of foo.

これは合法的なものです。 xy は同じ具象型を持ちます。これは == では、両方のパラメータが Self .

protocol Equatable {
  static func == (lhs: Self, rhs: Self) -> Bool
}

これは、具体的な適合型と同じ型である2つの値を期待することを意味します。たとえ Equatable が型として使用可能であったとしても、二つの任意の Equatable に準拠した値を互いに比較することはできないでしょう。

func foo(_ x: Int) -> Equatable { // Assume this is legal.
  if x > 10 {
    return 0
  } else {
    return "hello world"      
  }
}

let x = foo(20)
let y = foo(5)
print(x == y) // Illegal.

コンパイラは二つの任意の Equatable の値が同じ基礎となる具象型を持つことを証明できないからです。

同じように、別の不透明な型を返す関数を導入すれば

//   foo() -> <Output1 : Equatable> Output1 {
func foo() -> some Equatable { 
  return 5 // The opaque result type is inferred to be Int.
}

//   bar() -> <Output2 : Equatable> Output2 {
func bar() -> some Equatable { 
  return "" // The opaque result type is inferred to be String.
}

let x = foo()
let y = bar()
print(x == y) // Illegal, the return type of foo != return type of bar.

この例では foobar を返す some Equatable また、その逆の一般的なプレースホルダーである Output1Output2 は異なるタイプによって満たされる可能性があります。


3. 不透明な結果タイプは一般的なプレースホルダーと合成される

通常のプロトコル型値とは異なり、不透明な結果型は通常の一般的なプレースホルダーなどとうまく組み合わせることができます。

protocol P {
  var i: Int { get }
}
struct S : P {
  var i: Int
}

func makeP() -> some P { // Opaque result type inferred to be S.
  return S(i: .random(in: 0 ..< 10))
}

func bar<T : P>(_ x: T, _ y: T) -> T {
  return x.i < y.i ? x : y
}

let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Legal, T is inferred to be the return type of makeP.

これは、もし makeP を返しただけではうまくいきません。 P として、2つの P の値が異なる具象型を持つ可能性があるためです。

struct T : P {
  var i: Int
}

func makeP() -> P {
  if .random() { // 50:50 chance of picking each branch.
    return S(i: 0)
  } else {
    return T(i: 1)
  }
}

let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Illegal.


なぜ具象型ではなく不透明な結果型を使用するのですか?

この時点で、なぜコードを次のように書かないのか、自分自身で考えているかもしれません。

func makeP() -> S {
  return S(i: 0)
}

さて、不透明な結果型を使用することで、型を S で提供されるインターフェイスのみを公開することで、実装の詳細とすることができます。 P によって提供されるインターフェイスのみを公開することで、関数に依存するコードを壊すことなく、後で具象型を変更する柔軟性を与えています。

例えば、置き換えることができます。

func makeP() -> some P {
  return S(i: 0)
}

を使っています。

func makeP() -> some P { 
  return T(i: 1)
}

を呼び出すコードを壊さずに makeP() .

参照 不透明な型」セクションを参照してください。 言語ガイドの Swift の進化に関する提案 を参照してください。