@_functionBuilder 在少于2个项时存在初始化问题

huangapple go评论93阅读模式
英文:

@_functionBuilder problem with initializer when there is less than 2 items

问题

我正在尝试使用Swift 5.1的@_functionBuilder来设置一个小型DSL,这里是我的问题,假设我有一个包含多个行的Section类型,并且我希望以SwiftUI中堆栈构建的方式构建它们。这是我的类型:

  1. struct Section {
  2. var rows: [Row]
  3. struct Row {
  4. let label: String
  5. }
  6. }

这是我的函数构建器,它接受多个行并将它们制作成一个部分:

  1. @_functionBuilder
  2. struct SectionBuilder {
  3. static func buildBlock(_ segments: Section.Row...) -> Section {
  4. Section(rows: segments)
  5. }
  6. }

以及我Section类型的扩展,以使用我的函数构建器构建一个部分:

  1. extension Section {
  2. init(@SectionBuilder _ content: () -> Section) {
  3. self = content()
  4. }
  5. }

这样,我可以像DSL一样构建我的部分:

  1. let section2 = Section {
  2. Section.Row(label: "first")
  3. Section.Row(label: "second")
  4. }

它运行得很完美,只有当我想要只有一行或没有行时会出现问题:

  1. let section1 = Section {
  2. Section.Row(label: "alone") // 无法将类型'Section.Row'的值转换为闭包结果类型'Section'
  3. }
  4. let section0 = Section { } // 无法将类型'() -> ()'的值转换为预期的参数类型'() -> Section'

但最奇怪的是,当我完全相同的操作而不使用初始化程序时,它可以正常工作:

  1. @SectionBuilder
  2. func getSection0() -> Section {
  3. }
  4. @SectionBuilder
  5. func getSection1() -> Section {
  6. Section.Row(label: "alone")
  7. }
  8. @SectionBuilder
  9. func getSection2() -> Section {
  10. Section.Row(label: "first")
  11. Section.Row(label: "second")
  12. }

所以,如果有人能解释我做错了什么,我倾听着!先行致谢。

编辑:我还尝试在SectionBuilder中添加这两个方法,但没有帮助...

  1. static func buildBlock(_ segment: Section.Row) -> Section {
  2. Section(rows: [segment])
  3. }
  4. static func buildBlock() -> Section {
  5. Section(rows: [])
  6. }
英文:

I'm trying to setup a small DSL with the Swift 5.1 @_functionBuilder, here's my problem, let's say I have a Section type containing multiple rows, and I wish to build them the way stacks are built in SwiftUI. Here are my types:

<!-- language-all: lang-swift -->

  1. struct Section {
  2. var rows : [Row]
  3. struct Row {
  4. let label : String
  5. }
  6. }

Here is my function builder, which takes several rows and make them into a section:

  1. @_functionBuilder
  2. struct SectionBuilder {
  3. static func buildBlock(_ segments: Section.Row...) -&gt; Section {
  4. Section(rows: segments)
  5. }
  6. }

And the extension of my Section type to build a section with my function builder:

  1. extension Section {
  2. init(@SectionBuilder _ content: () -&gt; Section) {
  3. self = content()
  4. }
  5. }

This way I can build my section DSL-like:

  1. let section2 = Section {
  2. Section.Row(label: &quot;first&quot;)
  3. Section.Row(label: &quot;second&quot;)
  4. }

And it works perfectly well, except when I want to have only one row, or none:

  1. let section1 = Section {
  2. Section.Row(label: &quot;alone&quot;) // Cannot convert value of type &#39;Section.Row&#39; to closure result type &#39;Section&#39;
  3. }
  4. let section0 = Section { } // Cannot convert value of type &#39;() -&gt; ()&#39; to expected argument type &#39;() -&gt; Section&#39;

But the strangest is that when I do exactly the same without using the initializer, it works perfectly:

  1. @SectionBuilder
  2. func getSection0() -&gt; Section {
  3. }
  4. @SectionBuilder
  5. func getSection1() -&gt; Section {
  6. Section.Row(label: &quot;alone&quot;)
  7. }
  8. @SectionBuilder
  9. func getSection2() -&gt; Section {
  10. Section.Row(label: &quot;first&quot;)
  11. Section.Row(label: &quot;second&quot;)
  12. }

So, if someone can explain what I'm doing wrong, I'm all ears!
Thanks in advance.

EDIT: I've also tried by adding those two methods in SectionBuilder, but it doesn't help...

  1. static func buildBlock(_ segment: Section.Row) -&gt; Section {
  2. Section(rows: [segment])
  3. }
  4. static func buildBlock() -&gt; Section {
  5. Section(rows: [])
  6. }

答案1

得分: 1

我已经找到一个部分但可用的答案!

对于单行部分,我只需添加此初始化程序:

  1. extension Section {
  2. init(@SectionBuilder _ content: () -> Row) {
  3. self.init(rows: [content()])
  4. }
  5. }

不幸的是,当我没有带有此代码的行时,它不起作用:

  1. extension Section {
  2. init(@SectionBuilder _ content: () -> Void) {
  3. self.init(rows: [])
  4. }
  5. }
  1. let section0 = Section { } // 无法为类型'Section'调用初始化程序,其参数列表类型为'(@escaping () -> ())'

所以我尝试使用逃逸闭包:

  1. extension Section {
  2. init(@SectionBuilder _ content: @escaping () -> Void) {
  3. self.init(rows: [])
  4. }
  5. }
  1. let section0 = Section { } // 表达式类型'Section'在没有更多上下文的情况下是模糊的

因此,由于我仅仅是为了语法糖而使用此DSL表示法,我可以选择:

  1. extension Section {
  2. init() {
  3. self.init(rows: [])
  4. }
  5. }
  1. let section0 = Section()
英文:

I have found a partial but usable answer!

For the single row section I just have to add this initializer:

<!-- language-all: lang-swift -->

  1. extension Section {
  2. init(@SectionBuilder _ content: () -&gt; Row) {
  3. self.init(rows: [content()])
  4. }
  5. }

Unfortunately, it doesn't work when I have no row with this one:

  1. extension Section {
  2. init(@SectionBuilder _ content: () -&gt; Void) {
  3. self.init(rows: [])
  4. }
  5. }
  6. let section0 = Section { } // Cannot invoke initializer for type &#39;Section&#39; with an argument list of type &#39;(@escaping () -&gt; ())&#39;

So I tried with an escaping closure:

  1. extension Section {
  2. init(@SectionBuilder _ content: @escaping () -&gt; Void) {
  3. self.init(rows: [])
  4. }
  5. }
  6. let section0 = Section { } // Expression type &#39;Section&#39; is ambiguous without more context

So, as it is merely for syntactic sugar that I use this DSL notation, I can go with:

  1. extension Section {
  2. init() {
  3. self.init(rows: [])
  4. }
  5. }
  6. let section0 = Section()

huangapple
  • 本文由 发表于 2020年1月3日 23:45:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/59581419.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定