Swift(UI)の「some」キーワードとは何ですか? 質問する

Swift(UI)の「some」キーワードとは何ですか? 質問する

新しいSwiftUIチュートリアル次のコードがあります:

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

2 行目の「 」という単語はsome、そのサイトではキーワードであるかのように強調表示されています。

Swift 5.1 にはキーワードがないようですsome。また、通常は型が配置される場所に配置されるため、この単語が他に何をする可能性があるのか​​わかりませんsome。Swift の新しい、未発表のバージョンがあるのでしょうか。これは、私が知らない方法で型に使用されている関数なのでしょうか。

キーワードは何をsomeするのですか?

ベストアンサー1

some View不透明な結果型紹介者SE-0244これは、Xcode 11 の Swift 5.1 で使用できます。これは、「逆」汎用プレースホルダーと考えることができます。

呼び出し元によって満たされる通常の汎用プレースホルダーとは異なり、

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を表すために使用できます。 S1S2P

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型として使用できたとしても、2 つの任意の準拠値を互いに比較することはできません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.

コンパイラは、任意の 2 つの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.

fooこの例は、 との両方がbarを返しますがsome Equatable、それらの「逆」汎用プレースホルダOutput1と はOutput2異なる型によって満たされる可能性があるため、不正になります。


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 の進化の提案この機能の詳細については、こちらをご覧ください。

おすすめ記事