英文:
@_functionBuilder problem with initializer when there is less than 2 items
问题
我正在尝试使用Swift 5.1的@_functionBuilder
来设置一个小型DSL,这里是我的问题,假设我有一个包含多个行的Section
类型,并且我希望以SwiftUI中堆栈构建的方式构建它们。这是我的类型:
struct Section {
var rows: [Row]
struct Row {
let label: String
}
}
这是我的函数构建器,它接受多个行并将它们制作成一个部分:
@_functionBuilder
struct SectionBuilder {
static func buildBlock(_ segments: Section.Row...) -> Section {
Section(rows: segments)
}
}
以及我Section
类型的扩展,以使用我的函数构建器构建一个部分:
extension Section {
init(@SectionBuilder _ content: () -> Section) {
self = content()
}
}
这样,我可以像DSL一样构建我的部分:
let section2 = Section {
Section.Row(label: "first")
Section.Row(label: "second")
}
它运行得很完美,只有当我想要只有一行或没有行时会出现问题:
let section1 = Section {
Section.Row(label: "alone") // 无法将类型'Section.Row'的值转换为闭包结果类型'Section'
}
let section0 = Section { } // 无法将类型'() -> ()'的值转换为预期的参数类型'() -> Section'
但最奇怪的是,当我完全相同的操作而不使用初始化程序时,它可以正常工作:
@SectionBuilder
func getSection0() -> Section {
}
@SectionBuilder
func getSection1() -> Section {
Section.Row(label: "alone")
}
@SectionBuilder
func getSection2() -> Section {
Section.Row(label: "first")
Section.Row(label: "second")
}
所以,如果有人能解释我做错了什么,我倾听着!先行致谢。
编辑:我还尝试在SectionBuilder
中添加这两个方法,但没有帮助...
static func buildBlock(_ segment: Section.Row) -> Section {
Section(rows: [segment])
}
static func buildBlock() -> Section {
Section(rows: [])
}
英文:
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 -->
struct Section {
var rows : [Row]
struct Row {
let label : String
}
}
Here is my function builder, which takes several rows and make them into a section:
@_functionBuilder
struct SectionBuilder {
static func buildBlock(_ segments: Section.Row...) -> Section {
Section(rows: segments)
}
}
And the extension of my Section
type to build a section with my function builder:
extension Section {
init(@SectionBuilder _ content: () -> Section) {
self = content()
}
}
This way I can build my section DSL-like:
let section2 = Section {
Section.Row(label: "first")
Section.Row(label: "second")
}
And it works perfectly well, except when I want to have only one row, or none:
let section1 = Section {
Section.Row(label: "alone") // Cannot convert value of type 'Section.Row' to closure result type 'Section'
}
let section0 = Section { } // Cannot convert value of type '() -> ()' to expected argument type '() -> Section'
But the strangest is that when I do exactly the same without using the initializer, it works perfectly:
@SectionBuilder
func getSection0() -> Section {
}
@SectionBuilder
func getSection1() -> Section {
Section.Row(label: "alone")
}
@SectionBuilder
func getSection2() -> Section {
Section.Row(label: "first")
Section.Row(label: "second")
}
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...
static func buildBlock(_ segment: Section.Row) -> Section {
Section(rows: [segment])
}
static func buildBlock() -> Section {
Section(rows: [])
}
答案1
得分: 1
我已经找到一个部分但可用的答案!
对于单行部分,我只需添加此初始化程序:
extension Section {
init(@SectionBuilder _ content: () -> Row) {
self.init(rows: [content()])
}
}
不幸的是,当我没有带有此代码的行时,它不起作用:
extension Section {
init(@SectionBuilder _ content: () -> Void) {
self.init(rows: [])
}
}
let section0 = Section { } // 无法为类型'Section'调用初始化程序,其参数列表类型为'(@escaping () -> ())'
所以我尝试使用逃逸闭包:
extension Section {
init(@SectionBuilder _ content: @escaping () -> Void) {
self.init(rows: [])
}
}
let section0 = Section { } // 表达式类型'Section'在没有更多上下文的情况下是模糊的
因此,由于我仅仅是为了语法糖而使用此DSL表示法,我可以选择:
extension Section {
init() {
self.init(rows: [])
}
}
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 -->
extension Section {
init(@SectionBuilder _ content: () -> Row) {
self.init(rows: [content()])
}
}
Unfortunately, it doesn't work when I have no row with this one:
extension Section {
init(@SectionBuilder _ content: () -> Void) {
self.init(rows: [])
}
}
let section0 = Section { } // Cannot invoke initializer for type 'Section' with an argument list of type '(@escaping () -> ())'
So I tried with an escaping closure:
extension Section {
init(@SectionBuilder _ content: @escaping () -> Void) {
self.init(rows: [])
}
}
let section0 = Section { } // Expression type 'Section' is ambiguous without more context
So, as it is merely for syntactic sugar that I use this DSL notation, I can go with:
extension Section {
init() {
self.init(rows: [])
}
}
let section0 = Section()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论