用Go开发跨平台GUI

本篇内容是根据2023年3月份#271 Cross-platform graphical user interfaces音频录制内容的整理与翻译

这一期与 Wails 和 Fyne 的创建者一起深入研究为不同架构和操作系统编写 Go 代码。

译者注: Wails的作者是在澳大利亚悉尼的威尔士人,github头像是威尔士的旗帜,Wails也是Wales的同音字


Fyne的作者则是苏格兰人,在其他编程语言领域也有很多贡献,Fyne来自苏格兰的一个地名



两个人应该能算老乡了, 另外巧合的是,两个项目此刻在github的star数都是28.4k


过程中为符合中文惯用表达有适当删改, 版权归原作者所有.



Mat Ryer: 大家好,欢迎来到《Go Time》。我是Mat Ryer,今天我们要讨论跨平台图形用户界面。和我一起的还有我的联合主持人 Natalie Pistunovich。你好,Natalie。

Natalie Pistunovich: 你好,Mat。新年快乐!

Mat Ryer: 谢谢!这是什么节日?

Natalie Pistunovich: 波斯新年,在春分这一天庆祝。

Mat Ryer: 啊,真棒。所以对于我们北半球来说,这就是白天开始变长的日子。

Natalie Pistunovich: 对,白天的时间开始变长了。

Mat Ryer: 是的。不过不是对所有人都这样,因为我们今天的嘉宾还有来自澳大利亚悉尼的 Lea Anthony,他是SafetyCulture的高级软件工程师,也是Wails的创建者。对吧,Lea?欢迎。

Lea Anthony: 非常感谢。很高兴再次来到这里。

Mat Ryer: 是的,很高兴你能回来。哦对了,澳大利亚的网络会有延迟吧?

Lea Anthony: 是的,会有一点。

Natalie Pistunovich: 因为夜晚开始变长了……

Lea Anthony: 没错。

Mat Ryer: 是啊,祝你好运。这真是有点奇怪…… 我有点不确定是否相信澳大利亚的存在。以后我们再聊这个。我从没去过,只是有点怀疑。我喜欢提出问题,仅此而已。

我们的另一个嘉宾是Andy Williams。他是来自苏格兰的技术领导者,有超过20年的软件工程师经验,也是开源贡献者。Andy,你创建了Fyne,这是一款用Go编写的图形用户界面包,你还曾是Enlightenment和Maven的核心开发者,这非常了不起。欢迎。

Andy Williams: 非常感谢,Mat。很高兴能在这里,也很高兴与Lea和Natalie一起。希望大家今天过得愉快。我很高兴听到白天时间要变长了,因为---你知道,我们这儿在很北边,能带来一点阳光就好了。

Mat Ryer: 是的,这很好。你的介绍也很礼貌。我希望以后的嘉宾都能注意到这一点。

Lea Anthony: 记住了。

Mat Ryer: [笑] 好的。首先,图形用户界面…… 你们怎么发音?你们说“GUI”吗?

Andy Williams: 我一直说“GUI”,我觉得这是最简单的表达方式,大家都知道你在说什么。直到有一次我看到有人做笔记…… 我在谈论一本关于GUI的书,他们在记录要去读什么书,结果他们写下了“gooey”(黏糊糊的),我就想,“哦,天哪,我们这样说反而增加了理解的难度。”

Mat Ryer: 是的,确实。而且,GUI的书…… 没人想读那样的书,对吧?你看不下去,因为它就像是…… [笑声] Lea,你说“GUI”吗?

Lea Anthony: 我是的。还有其他说法吗?

Mat Ryer: 也有“G U I”的说法。我听过。

Lea Anthony: 不……

Mat Ryer: Natalie?

Natalie Pistunovich: 我说“GUI团队”。

Mat Ryer: 我喜欢这个。GUI。这个词还有点可爱。我确实曾在一个地方工作过,那里有一个团队非常强烈地认为后端工程才是“成年人的工程”,而前端只是把东西弄得好看…… 他们把“GUI”当成一种贬义词,真是遗憾。

Lea Anthony: 哇……

Mat Ryer: 是的。

Andy Williams: 我也听过有人把图形开发者和“GUI”称为“用彩色铅笔的骑手”,但大概并不是出于好的意图。

Mat Ryer: 是啊。我不知道为什么;这很有趣。我确实觉得这种看法已经改变了。过去确实有这种想法---你有时还能在Twitter上看到,有人为了赚取点击率或曝光度,会说“HTML不是真正的编程”或者“CSS是编造出来的”,这其实也不算错,它确实是某种程度上的编造。但我认为前端工程在很多方面实际上更难,视问题而定。至少在后端,你有相对一致的输入输出。而前端呢?谁知道用户会做什么?他们是人类,非常不可预测。他们可能点击任何东西。Natalie,你说“GUI”吗?

Natalie Pistunovich: 我说“GUI”,我还认为有时机器也会点击界面,对吧?这就是我们有验证码检查的原因。

Mat Ryer: 是的,没错。

Natalie Pistunovich: 我们不希望机器点击某些东西,这就是为什么我们要确保它们是人类。

Mat Ryer: 让我想到,或许在API编程接口上,我们应该有一个问题:“你是人类吗?” 然后你必须回答“不是”才能通过。

Natalie Pistunovich: 你刚刚发明了用户代理。[笑声]

Mat Ryer: 好的,我觉得我们已经解决了重要的问题。我们将称它们为“GUIs”。不过,你们一般不会把Go和构建GUI联系在一起,对吧?Go通常被认为是一种系统语言,用于后端服务、API之类的东西。但其实你是可以用它来构建GUI的,对吧,Lea?

Lea Anthony: 没错,你可以。我认为Go是一种通用编程语言,和大多数语言一样,那么为什么不呢?用Go开发GUI有什么不好的地方吗?

Mat Ryer: 我不知道。我不觉得有什么不好。Andy,你怎么看?

Andy Williams: 我完全同意。Go非常适合开发GUI。有人可能会说:“这不是这门语言的初衷。” 但正如Lea所说,作为一门通用语言,它的有用性并不由其标准库或最初的设计目的决定,而是由人们发现它在哪些地方合适,以及在哪些领域获得了发展。从我看到的情况来看---我相信在这通话中的其他人也有类似的感觉---人们正在积极使用Go来开发GUI,并且发现它非常适合。如果你不再把它仅仅看作是为服务器而设计的语言,而是看它的内存管理、垃圾回收、静态类型等特性,它帮助你定义非常明确的API,整体上非常可靠。而且,如果Go能解决所有这些跨平台的挑战,为什么不也用它来解决图形应用程序中的最大挑战之一---让应用程序能够运行在所有不同的平台呢?我认为Go非常合适。

Mat Ryer: 是的。或许我们可以深入了解一下你们的项目,你们各自的项目都提供了跨平台图形界面的支持,但实现方式却非常不同。Lea,对于不熟悉Wails的人,你能告诉我们怎么拼写它,以及它是什么、如何工作吗?

Lea Anthony: 好的,Wails的拼写是W-A-I-L-S。这是以前的一个文字游戏。当时我发现了一个叫WebView的项目,我想用HTML创建一个图形应用程序,原因是我在日常工作中已经在使用它,所以这对我来说是熟悉的。我发现了WebView,它很好用,但我想要更高层次的体验。它缺少很多东西,所以我想,“为什么不自己写一些功能,构建IPC(进程间通信),并实现前后端之间的通信呢?它们是两个不同的世界。” 所以我花了很长时间来做这件事,感觉有点像Rails,或者说是像Ruby on Rails的那种感觉…… 所以这是一个文字游戏的结果。我有点后悔,但也不完全后悔。所以项目就这样演变了…… 就像这个项目一样。

Mat Ryer: 而且你还来自威尔士(Wales)。

Lea Anthony: 是的。你会以为我会想到这一点吧?[笑声] 但我没有。有一天我突然意识到,“哦,或许这就是灵感来源吧。” 但这个项目其实是为那些想用Go编写HTML应用程序的人设计的,因此它提供了很多相关的工具。

Mat Ryer: 所以你基本上是在写网页前端,对吧?

Lea Anthony: 是的。我认为Go在生成网站应用程序方面还是有一定份额的,所以这两个世界之间有一点关联…… 不过,它已经逐渐演变成了一种类似Electron的东西,现在有系统托盘菜单、应用菜单、对话框等。这并不是我最初的目标,最初只是为了满足我自己写程序的需求。有趣的是,在这六年或四年的时间里,我还从未真正用它写过一个程序。所以…… 是的,构建这个工具集实在太有趣了,我就迷失在其中了。

Mat Ryer: 太棒了。我喜欢这种现象,就像你说的,你是在解决自己的问题…… 你经常会听到类似的故事,这也是我一直给年轻工程师的建议,特别是那些刚入行的工程师,就是要解决你自己的实际问题。真正解决问题才是我们在做的事情,而技术只是实现这一目标的手段。

Andy,Fyne…… Fyne的工作方式和Wails不太一样,对吧?它采用了稍微不同的方式。

Andy Williams: 是的,完全正确。如果有人不知道的话,我们也选择了一个奇怪的拼写,它是F-Y-N-E,而不是你一开始会想到的拼法…… 但它有一点地理上的联系。这是苏格兰的一个地方,所以我们觉得挺合适的。

Mat Ryer: 就像Wails和Fyne…… 这真的很棒。

Andy Williams: 我知道。灵感常常来自于意想不到的地方。你说得对,当我们在设计Fyne时,确实走了一条不同的路线。我过去做了很多原生应用开发,但我也曾使用跨平台工具做开发,尤其是在Enlightenment项目中;这些工具有优点也有缺点。Apple的工具包确实有一些很棒的优势,但当时它主要是单平台的…… 我还见过一些工具的兴衰,比如Swing等(译者注: Swing是Java生态知名的图形界面工具)。然后有一天,我坐下来想:“其实,应该有一种更好的方式,可以用一种编程语言编写出符合惯用法的代码,并且能够在任何平台上运行。” 于是,我就采用了这个思路。我的基本架构非常简单,设计了一个简洁的API结构,之后我接触到了Go语言,发现它非常适合这个架构。巧的是,我在Go语言中没有找到类似的东西,这当然是一个小小的惊喜…… 你说得对,这确实使得开发者端的应用程序完全用纯Go编写。所以无论你是否喜欢这种方式,它都和Go语言契合得很好,你只需使用标准的结构体、接口、方法和函数来编写代码。这一方法既保持了代码的可维护性,也带来了一些额外的好处…… 比如,我们可以直接利用Go语言中非常强大的单元测试框架,因为我们使用的是语言本身的构造。

因此,从我想要实现的目标来看,Go确实是一个不错的选择,我也很喜欢这条路线…… 当然,它也有一些权衡,因为我们没有像基于HTML的解决方案那样获得一些实时编辑的功能。所以我们正在构建的是一些稳固的、经过良好工程化的应用程序…… 这正是Go擅长的领域。

Natalie Pistunovich: UI的测试是什么样的?你会测试些什么?

Andy Williams: 基本上,Fyne的部件(widgets)都提供了基于行为的API。因此,你的单元测试会测试这些行为。比如,你可能会创建一个输入框,然后你可以模拟在输入框中输入文本,之后验证文本是否正确。你还可以使用我们的测试包模拟双击输入框,并通过检查选中的文本来验证文本是否被正确选中。我们使用了很棒的单元测试框架,当然有很多可供选择的框架;Testify可能是我们使用的一个框架。通过这些测试,我们能够非常快速地验证功能是否正常。

不过我们也意识到,有时候你确实需要看到实际的效果。因此你可以测试渲染的状态,把渲染结果保存为图像,然后进行图形比较,甚至可以通过程序进行比较。或者,因为反锯齿和其他问题可能导致不精确的结果,你也可以使用XML树来进行比较。不过,真正好的一点是,这些测试执行得非常快,因为它们是基于内存的单元测试,根本不需要显示在屏幕上,这带来了很大的便利。

Natalie Pistunovich: 那当你测试某些操作时,比如你刚才提到的双击按钮---这种情况是模拟双击事件发生吗?还是你有一些外部脚本来模拟双击操作?

Andy Williams: 我们并没有通过任何外部系统。因为它是在内存中执行的,实际上发生的事情是我们加载了一个测试驱动程序,它模拟了应用程序的运行。所以你的部件会在内存中的应用程序中完全加载,但不会渲染到屏幕上。当你说“双击这个部件”时,它会触发事件链,就像用户在屏幕上双击了一样,而不需要通过操作系统的连接。

Natalie Pistunovich: 这真酷。我必须说,对于那些没有看直播而是之后听的人,Mat有一台摄像头会跟随他移动。他来回走动时,摄像头就会跟着他移动。所以你可以像看熔岩灯一样看着他移动…… 这解释了从现在到节目结束之间的所有尴尬的沉默。

Mat Ryer: [笑] 是的,这是Apple Studio显示器之一,它有一个广角摄像头,然后通过软件跟踪你的脸。如果有其他人进入画面,它会自动拉远…… 它还不错,只是有时候你想暂时离开镜头,但可能会忘记它会一直跟着你。

Andy Williams: 现在Natalie提到了熔岩灯,我无法不联想到这个,因为Mat的红色毛衣真的很契合这个描述…… [笑声]

Mat Ryer: 是的…… 我就像这样漂浮着……

Natalie Pistunovich: 如果这还不足以让人们开始看直播,我不知道还有什么能做到。

Mat Ryer: 是啊。我想我们也在YouTube上直播。你可以直接观看---对吧,Natalie?我不太确定。

Natalie Pistunovich: 是的,是的。我们在YouTube上直播,至少有一个人确认正在观看。嗨,Mike Dodson。我们很感谢你。

Mat Ryer: 哦,嗨,Mike。不过本质上这是一个播客,不只是为Mike……

Natalie Pistunovich: 但今天我们在讨论视觉效果。

Mat Ryer: 是的,我们确实在讨论视觉效果……

Natalie Pistunovich: 我们还可以讨论音频。我注意到,Lea,在你的仓库描述中,你提到了你在编写代码时听的所有专辑……

Lea Anthony: 是啊,我也不知道为什么要这么做,但我想我在其他仓库中看到过类似的东西,觉得这很有趣。这是一个分享一些不太知名乐队的好方法。我想那时还没有流媒体服务,所以人们主要通过推荐找到音乐…… 不过我也不确定为什么要这样做,但确实挺有趣的。有意思的是,有些人实际上联系我说:“我听了这个乐队,真的很棒。” 所以…… 你们有人做过这种事吗?你们没在其他仓库里见过吗?

Mat Ryer: 不,我从未见过。不过如果你去github.com/wailsapp/wails……

Natalie Pistunovich: …你也可以在节目笔记中找到这个链接……

Mat Ryer: 是的,你会在节目笔记中找到这个链接…… 但说真的,有多少人会读节目笔记?你们会读吗?我不读节目笔记。

Natalie Pistunovich: 我写了它们。 [笑]

Mat Ryer: 对,你写了它们。 [笑] 谢谢你。是的,单元测试的内容很有趣。在Fyne中,前端代码本身是什么样的?你最终会有一堆嵌套的对象来描述用户界面吗?

Andy Williams: 是的,如果你喜欢这种方式的话,你完全可以这样做。你可以将用户界面的对象结构作为一个大的结构体来编写;我们支持这种方式,就像你在Go代码中处理任何复杂的结构化数据一样。不过,构造函数的使用帮助我们将其拆分为更小的组件,我个人很喜欢这种方式。我们的设计原则之一是保持封装性,所以无论是标准部件还是任何第三方部件,都可以在任何上下文中重用。所以,如果你按照这种方式编写代码,那么你可能会有一个函数来设置主屏幕,这个函数可能会调用不同的函数,将面板插入到父容器的正确位置,例如“构建我的工具栏”或“设置我的树形布局”。我喜欢鼓励这种做法,因为我喜欢简洁的代码,喜欢小型的、只做一件事的函数,这种方式非常契合我的编程风格。不过,我通常会有一个结构体,比如一个应用程序结构体,或者每个屏幕都有一个结构体,用来跟踪一些重要的元素,比如输入框或输出部件,以便稍后引用;这很有帮助。

这种可插拔部件的优势与Go语言能够从任何地方导入第三方代码的能力完美契合。你可以通过导入第三方组件并调用其构造函数,将其直接嵌入你的UI中。比如我们的终端部件---你可以在任何Fyne应用程序中嵌入一个全功能的终端,因为它是一个可以随时嵌入的部件。这种灵活性在原生应用开发中是非常罕见的,这也是我非常珍视的设计之一,而这也是多亏了Go语言的设计才得以实现。

Mat Ryer: 是的。Lea,在Wails中,当你构建前端时,它是什么样的?是HTML、JavaScript…… 还有CSS吗?

Lea Anthony: 是的。你可以像开发任何一个网站一样开发应用程序。你可以选择npm生态系统,或者你喜欢的其他生态系统。你可以使用现有的工具包…… 这没什么好说的,如果你曾经开发过网站,那么你就可以开发一个应用程序。

Mat Ryer: 是的。我喜欢这种技能的可转移性。当然,总有一些东西需要学习,但如果你会Go语言,又会做网页前端,那么你就可以上手这些工具。我觉得这非常好,也非常重要。开发者的体验在这些工具中非常重要,因为传统上,尤其是构建跨平台的图形用户界面,还是有些复杂的。我曾为macOS写过一些代码,试图与操作系统交互,确实不容易;你经常会感觉自己在舒适区之外。而这些框架很好地解决了这个问题。

不过,告诉我一些关于跨平台的信息…… 对于许多人来说,无论是GUI应用还是其他,我们可能会遇到编写跨平台代码的需求。因为Go可以为所有平台构建,所以这很容易吗?尤其是图形化相关的部分---告诉我,这容易吗?

Lea Anthony: 是的。

Mat Ryer: 是这样吗?仅仅因为你只需要写一次代码,它就能正常工作?但不可能吧,对吗?

Lea Anthony: 我认为当然可以。这是我们都在追求的梦想。而且,绝对是可以实现的。我不会说为工具包开发这个目标很简单,但我们的目标正是如此。你有一个应用程序,你可以在你的系统上运行它,并且知道它在其他系统上也会完全相同地运行,无论是不同的架构、不同的操作系统,还是桌面、平板设备、手机等---只要目标平台被支持,你基本上可以保证应用程序会以完全相同的方式工作。

Mat Ryer: 对于用户来说,这很重要。那么,告诉我一下构建这个过程吧。写跨平台的代码---无论是写GUI还是其他,Go的开发者只需针对不同的架构编写代码。当然,你可以为任何架构构建代码,但当你与前端组件交互时,每个操作系统的情况不都不一样吗?

Lea Anthony: 我认为,前端组件的交互本身在不同的操作系统中并没有太大的不同,但每个操作系统提供的东西确实略有不同。例如,文件对话框。在一个操作系统中,你可能可以在本地对话框中创建目录和文件夹,但在另一个操作系统中,你可能无法做到这一点。这种本地工具包之间的差异---我指的是操作系统提供的内容,不是我们自己的工具包---我认为弥合这种差异是相当困难的。Electron的处理方式是,它基本上向你提供了所有选项,然后你需要自己决定每个选项适用于哪个操作系统,它会告诉你“这是用于Mac的”或“这是仅用于Windows的”。我认为这是一个经典的问题,没有好的解决方法。

我早期采用的一种方法是,如果某功能不能在所有操作系统上实现,那么干脆不做,这样你就能在所有平台上保持一致。但后来用户会回来问,“那这个功能呢?” 比如我们做xbar时就是一个很好的例子。在Mac的托盘图标中可以显示文本,但在Windows中却不能。所以,如果你编写一个需要显示文本的应用程序,那么移植到Windows时就会遇到一个很大的问题。我相信还有很多类似的例子。

Mat Ryer: 是的。所以,这涉及到在设计抽象层时要做得好,这是你面临的挑战之一…… 你是选择最小公分母的方式,确保所有地方都能正常工作?还是有某种功能API来处理,如果调用了不支持的功能,是否在运行时抛出错误?它是如何工作的?

Lea Anthony: 我觉得我们可能会有不同的答案……

Andy Williams: 是的,是的。我认为这不一定是运行时错误,而是某些功能只是没有被支持。所以,如果你打开一个文件对话框并请求能够创建目录,你可能会得到一个不支持该功能的对话框。这是没办法的事情。所以,我认为目标二进制文件在不同平台上会有略微不同的功能,但代码可以是相同的。也就是说,Go代码可以是相同的,但……

Mat Ryer: 那对于某些功能在一个操作系统中存在而另一个操作系统中不存在的情况呢?有没有这种情况?

Andy Williams: 我正好可以回答这个问题,因为这是与原生系统能做什么和不能做什么相关的。在Fyne中,我们并不是追求最小公分母。我们实际上提供了很多功能来替代本地系统应该做的事情。

例如,我们有一个文件对话框,它会显示为使用我们工具包构建的应用程序的对话框,而不是本地系统的对话框,这意味着你在那些没有完整图形支持的系统上也有一致性和完全可用的应用程序。我特别指的是一些像uBSD这样的系统,可能是Open或NetBSD,它们并不是完全图形化的平台,但Fyne应用程序可以直接加载并运行,而且完全正常工作。所以我们不得不编写大量功能代码,以填补这些空白。

当然,有些功能不可能在所有平台上都可用。移动设备有一些特定的功能,它可以提供,而桌面系统则没有,反之亦然。你提到的xbar和系统托盘的概念很有趣,因为移动设备实际上并没有这个概念。当然,有通知区域,技术上可以做一些类似的事情,但概念上并不相同。

实际上,在Fyne API中,我们借鉴了类型检查的概念,使用接口来提供扩展功能。所以应用程序接口提供了所有在任何系统上都能正常工作的功能,比如你可以显示通知、打开窗口或在本地浏览器中打开URL等。但系统托盘不在其中,因为我们不能保证它能正常工作。

所以我们有一个desktop包,它定义了应用程序的扩展功能。你可以在代码中进行类型检查,所以在运行时应用程序的实现会有所不同,但你的代码可以通过编译器检查,确保你不会做出可能在运行时导致崩溃的操作,或者使用不可用的功能。我不会说我们每次都能做到完美,抽象层可能会有问题,但我们非常努力地确保如果你在IDE中可以自动完成,那么它就一定可以运行。

Mat Ryer: 这很有趣。所以这些是接口,你通过类型断言和第二个参数(即ok)来检查,从而知道某个功能是否可用?

Andy Williams: 是的,没错。你可以对你的应用实例进行类型检查,比如对desktop.app进行类型检查,这样你就可以访问系统托盘。如果你检查的是mobile.app,它会暴露设备的相关信息,比如屏幕方向和一些其他传感器,这些在桌面设备上是没有的。

Lea Anthony: 那这会根据你正在开发的平台而改变吗?比如当你在Windows上开发,并且想要在托盘图标上显示文本,当你输入点号时,看到的内容和其他平台一样吗?

Andy Williams: 这是个很好的问题。如果你的IDE能理解你当前正在测试的目标平台,那是可以的。所以你需要在IDE中设置一些变量,调整它以适应你想要查询的平台,这样你就可以“欺骗”它,让它认为类型断言会生效或不会生效,取决于已设置的类型。所以,也许自动完成并不总是最好的方式。不过,话虽如此,它应该仍然有效,因为它知道接口,只是不知道类型断言是否会成功。所以大多数情况下,它应该是可行的。当然,我相信有些情况下可能不那么直接,但我认为---大体上,它是可行的。

人们会问:“我觉得我发现了一个问题。我做了这些事情,但它没有达到预期的效果,所以这是已知问题吗?” 我希望能说,“如果你在代码中告诉它做某件事,而它没有做到,那么这可能是一个bug,而不是某种配置错误或用户失误。” 因为我们努力保持API的简洁,并确保它是易于理解的,基本上可以保证能正常工作。

Natalie Pistunovich: 引入泛型后,你们的工作是否变得更轻松了?

Andy Williams: 说实话,泛型的引入暂时还没有影响到我的工作……我知道它会让某些事情变得更好,但我很乐意坐在一旁,等待其他人努力探索它的真正好处。所以我们有一些数据绑定API,可以帮助你将数据与前端的图形元素直接连接起来,这样你就不需要编写任何样板代码,也不需要处理事件或变更。至少在内部,泛型会改善这一点,因为我们目前是通过代码生成来实现的。然而,目前我们还没有准备好采用泛型,因为我们支持的Go版本比引入泛型的版本要早很多……部分原因是我希望人们能够使用他们系统上默认安装的Go版本来构建他们的应用程序。而有些系统的Go版本还没有更新到足够新的版本。我认为Debian稳定版上仍然使用的是1.15版本,所以我们在等待他们的下一次发布,届时可能会更新到1.18版本……

另一个原因是我们仍然支持Go 1.14 API,因为这是最后一个支持苹果ARM-32设备的版本。我的内心有一部分实在不愿意因为编译器团队同意了苹果的观点,而将数百万设备推向废弃堆;这些设备被认为是遗留设备。

所以我们最终会做出改变,当然人们也在推动这个改变,因为新语言特性确实很棒……所以在今年的某个时候,我们会升级我们支持的最低版本。不过,即便如此,我仍然不完全相信泛型会对我们的API产生巨大的影响。

Mat Ryer: 是的。Lea,Wails的思路也是类似的,你可以绑定类型。我猜你现在是使用接口。泛型会对你有所帮助吗,还是会改变你的现状?

Lea Anthony: 嗯,我也不确定。我觉得,像Andy一样,我也只是静观其变,看看泛型会带来什么变化。对我来说,泛型最好的用例是数据结构中的一些东西,这些东西你不一定会直接处理。是的,在某些情况下,它确实很有用,比如你想在不同类型之间重用某些功能,而且你希望能够重用这些代码……但我还没有看到太多地方能用到它。我一直在考虑开发一个状态存储的概念。

我是Svelte的忠实粉丝,Svelte中的状态存储非常简单和实用,我有点喜欢这个想法,希望能在Go中使用它,作为一种桥梁。所以你会有一个通用的状态存储。我之前曾实现过一个,它使用了接口(空接口),但存在一些问题。你需要让开发者自己处理所有的类型转换问题,还要处理如果你想更改这些东西会发生什么……这不是一个好的解决方案。我想泛型可能会有帮助,你可以创建多个存储,存储不同的东西,并且可以使用相同的代码……我还在研究这个问题,这不是我花了很多时间去研究的东西。我不确定,答案还没有定论。

译者注: “Svelte 是一种构建 Web 应用程序的新方法。它是一个编译器,可以获取声明性组件并将它们转换为高效的 JavaScript,从而彻底更新 DOM。” 上述内容来自 https://github.com/sveltejs/svelte

Natalie Pistunovich: 这是个有点意外的回答,因为在我听过的关于泛型利弊的讨论中,很多时候正面例子都是“用户输入”,我原以为GUI领域会听到两个“是的”和两个“还没有”。但你们的回答很有新意。

Mat Ryer: 是啊,我们需要深入探讨一下这个问题;我们要做一期Go Time的节目,讨论一下泛型的现状,看看它的发展情况,以及人们如何使用它,或者有没有滥用……这很有趣。

Mat Ryer: 如果你今天重新开始这些项目,你会有什么不同的做法吗?

Andy Williams: 哇,我立刻想到了一些事情。我们很早就稳定了API。我们现在处于1.0版本,可能是开发的第二年……很多人批评我们,“太早了,你不可能满足语义API保证,并且持续支持下去。” 但我们成功了,没有出现意外的破坏性变更。不过几年前我们确实需要引入v2包来做一些破坏性变化……所以对我来说,最简单的答案就是我不会在第一次API设计时犯那些错误,尽管我们花了一段时间才理解其中的原因。背后的问题是我们意识到要跨平台渲染内容的复杂性,当你面对如此多样化的屏幕输出类型时,像像素密度、尺寸等各种差异……

最初的问题是我以为输出类型应该是整数,用来定位屏幕上的像素,因为像素是比较标准的。如果我们要放大,我们会遵循苹果的主张,使用一倍、两倍、三倍图像来适应更高的像素密度屏幕。我们按照这个模型运行了一段时间,效果还不错,但后来问题开始出现了,因为我们引入了一个动画API,如果你使用移动动画,它会将某个对象从一个位置移动到另一个位置。而当你的输出设备精度是整数级别时,放大后中间的数值就不存在了。动画会跳跃、跳跃、再跳跃,所以我们不得不将其改为基于浮点数的系统。我真希望当初没有犯这个错误,因为这是我们必须升级的一个真正的破坏性改变。

但当你不完全理解自己进入的领域时,那些复杂性会被忽视……万一有年轻的、有影响力的开发者在听,不要自己构建图形工具包。这么做真的不安全,还是放弃吧。

Mat Ryer: 或者,如果你要做,或许你的建议应该是“永远使用浮点数”。

Andy Williams: 嗯,我不确定我会坚持这个观点;如果我说永远使用浮点数,可能有人会收回我的软件工程师执照。

Natalie Pistunovich: 我们也会从你手中拿走硬件,比如FPGA。

Andy Williams: 是的。

Mat Ryer: Lea,你是用整数还是浮点数呢?

Lea Anthony: 很有趣,Andy 提到苹果工具包时,我注意到他们在很多我意想不到的地方使用了浮点数。我其实并不太明白为什么要这么做,所以谢谢 Andy,我学到了新的东西,太棒了。

Andy Williams: [笑] 太好了。

Lea Anthony: 是的,我想如果我重新开始开发 Wails… 我可能犯的最大的错误,就是出于善意,试图让开发者的体验尽可能简单。问题不仅在于库本身,还在于围绕它的所有工具,以及你试图简化的所有事情。你会遇到各种问题,比如“我怎么快速启动一个项目?我如何开发应用程序的某些方面?我如何在运行时开发它?我如何实时运行和修改它?我如何实时修改 UI?”因此围绕这些问题有很多工具,这些工具大多是出于善意而开发的,并且大多数情况下运作良好。

能够打开浏览器并使用你选择的语言或工具包的扩展,这很棒。所以能够做这些事情,并提供所有这些功能,确实很棒。然而,我要说,可能 90% 的错误报告都出现在工具上,而不是库本身。所以我们实际上正在考虑将这一部分功能回退一点,不再让工具像一个黑箱,执行你意想不到的各种操作,而是将很多功能简化为独立的操作。例如,如果你想转换图标,只需执行一个操作。我们将使用 Taskfile,不知道你是否听说过,它有点像用 Go 写的 make。我们正在使用它来协调你通常会执行的所有操作。希望这能把更多的权力还给开发者,让他们有灵活性去选择自己想使用的工具,构建他们想要的流水线。所以,回过头来看,这可能是我希望能早一点做到的。

Mat Ryer: 这很有趣。我确实喜欢你对开发者体验的关注。我认为这是一个重要的教训,任何构建自己希望他人使用的包的人都应该学习。因为这些开发者就是你的用户,而用户体验是至关重要的。

Lea Anthony: 这关系到其他人使用你的工具时的体验,这让我想到另一个我可能会做得不同的地方… 这适用于任何库---那就是尽早让更多的社区成员参与进来。不要试图自己构建一个完整的演示版本,试着让大家早期就能参与进概念设计。无论他们是否在这个领域有经验,或者他们是否完全是小白,他们都会对你正在做的事情有很好的反馈,并且可能还会帮你实现它。我们的社区,实际上整个 Go 社区,都非常支持并愿意帮助。所以有他们的参与是非常棒的,但我认为我在早期并没有利用好这一点。我当时的想法是“我先做一个版本,证明这是可行的。”

Mat Ryer: 我能理解,但我觉得你说的这个点非常好。这也是一个在很多地方都适用的教训。我认为这对那些刚刚进入科技行业并想要参与某些项目的人来说也是个好消息。因为他们常常觉得“我能做什么?我有什么用处?” 但实际上,他们的视角非常有价值。他们不知道自己的视角有多么珍贵,因为有时候,当你在这个行业待了很久,你积累了很多知识,甚至自己都不太意识到,而这些知识会影响你所做的很多事情。所以,如果你是新人,这实际上是一种超级能力,特别是当你对某些事情不太了解的时候。所以你永远不该觉得自己没用。

Lea Anthony: 再往前推一步… 这可能是---这是我第一个开源项目。我是一名软件开发者,而不是社区维护者,我通过一些艰苦的经历学到了如何处理开源项目的一些方面。我认为,如果你要开始一个开源项目,有一件事你应该尽早确定,那就是如何进行互动。如果你想要一个新功能,或者你想报告一个错误,应该有一个明确的流程。因为获取所有这些意见是一把双刃剑。一方面,它带来了一些非常好的创造力,但你也需要设置一些边界和指南,来管理这个过程,因为你不可能接受所有东西,你不会接受每个人的想法。你会认为有些想法很好,有些则不然。所以你需要有一个框架来管理这一切,否则你的项目可能会走向奇怪的方向,最终变得难以维护,或者你可能会为某个极端情况开发出非常特定的功能。我不知道 Andy 你是怎么处理这些问题的,但对我们来说,这一直是一个挑战。

Andy Williams: 你说得对,这确实很难,但也非常重要。而且,不是每个人都会很开心,尤其是当他们的建议没有被采用时。不幸的是,事情有时会变得个人化。因此,我认为非常重要的一点是,讨论想法或概念时,尽量与提议的人区分开来。如果你有两三个人在讨论某种方法的利弊,请确保你是在描述和讨论这个想法,而不是提到“这是某个人提出的”。这样你就可以避免让讨论变得个人化。

除此之外,正如你所说,仔细定义这些流程---社区指南、贡献者指导---这样当你不得不拒绝某些建议时,你可以说“因为这些原因,它不符合我们的做事方式。” 我有一个原则适用于核心贡献者和首次贡献者:如果我们找不到一个明确的规则或指南来解释我们为什么不接受某个提案,那么说明我们的规则需要更新。接受某些东西或拒绝某些东西,绝不应该是主观判断。应该是“质量不够,因为我们期望达到某个标准”,或者“这个 API 不符合我们的设计风格,因为我们是这样设计我们的 API 的。” 这确实能帮助我们。

这种方式不一定在每个情况下都适用,有时候你可能会有自己的一些偏好,尤其是作为维护者或创建者的一部分。你可能会觉得有更好的方法,或者你脑海中已经有了一个计划。但在这种情况下,你可以帮助贡献者,让他们的代码更符合你认为的未来方向。这种情况并不常见,有时它略显主观,但你可以说“因为我们计划做的另一件事,它可能会与此冲突。因此我们能不能找另一种方式来探索这个问题?” 通常人们能理解这些,因为他们并不了解项目的所有规划。但即便如此,你分享得越多,他们就会越少感到惊讶,贡献也会更有方向性。

Mat Ryer: 那么,关于个人品味的问题呢,Andy?有时候,选择并没有对错之分,但你只是更喜欢某种方式。你是怎么处理这种情况的?

Andy Williams: 我认为这归结于达成共识,社区有某种偏好,并且这些偏好被记录下来了。当然,如果我们是一个较老的项目,使用的是一个不那么先进的语言,我们可能会讨论“应该用 Tab 还是空格?” 但我们已经摆脱了这些纠结,因为大家都同意 Go 的格式化标准是正确的。老实说,我不认为 Go 代码的格式是我以前喜欢的格式,但我也不太在意,因为这是写 Go 代码的标准方式。如果你能把所有主观的东西都这样表达出来,比如“这就是我们社区的规则,这是贡献的规则。如果你不喜欢,我们完全理解…但请按照我们的方式来,否则我们将退回 PR。” 因为老实说,接受某人的工作然后修改以适应项目的标准,这是一件很糟糕的事情。首先,这会给核心项目的成员增加额外的工作。而且,我听说有些贡献者会因此感到沮丧,因为他们提交的东西被立即修改了,他们会觉得自己的工作被质疑了,或者没有达到他们事先不知道的更高标准。

Mat Ryer: 有趣。嗯,你经常需要说 “不” 吗?

Andy Williams: 是的… [笑] 不过我会尽量说“因为某些原因所以不行”,或者如果可能的话,我会说“我在某个地方有疑虑”,这样就不完全是否定,但也不是肯定。

Mat Ryer: 很外交的回答。

Andy Williams: 当你有这么多白发时,这种情况遇得多了,你也就学到了这些经验。社区中有一位我合作过多年的朋友,他曾对一个新人描述我带领的项目时说:“你知道的,可能会有一些看似难以接受的反馈,但它们是经过深思熟虑的,理解它们很重要。如果你愿意接受这些指导并更新代码以适应项目,你会被社区接纳。如果你不愿意,那你可以维护自己的分支,或者为另一个社区贡献,或者保持自己的项目。” 有些人确实这么做了,这也是开源的美妙之处---你可以在核心社区中,也可以在它的旁边,维护一个分支,或者一些其他人可以使用的组件。你不一定要完全赞同一个社区的结构。

Mat Ryer: 这很好。Lea,我觉得你可能更直截了当。

Lea Anthony: [笑] 你为什么这么说?

Mat Ryer: 因为我了解你。

Lea Anthony: [笑] 不,这确实很难。就在最近,有人提交了一个 PR,但没有先开 issue,而且这个 PR 更像是个人偏好的修改。最后,我不得不说:“我们可能不会在这个版本中加入这个功能。这是一个有意思的想法,但这也是我需要维护的内容,而很多 PR 的一个常被忽视的方面就是谁来维护它。所以我建议你可以在自己的分支上运行这个修改,并使用它。” 正如 Andy 所说,这就是开源的美妙之处。你不会被项目的方向或意见所限制,你可以选择按照自己的方式行事,这完全没问题。

Andy Williams: 我记得有一次和另一位优秀的社区领导者的对话,当时我刚开始 Fyne 项目,我说:“我们要有一个干净的 API。每个人都可以轻松上手并构建自己的应用程序。我们会添加很多新功能,开发应用程序从未如此简单。” 他看着我说:“好吧,听起来你有一个很好的开始。但十年后再回来告诉我,你的 API 是否仍然像现在一样简单。” 所以有时候拒绝某个功能的原因只是“这超出了我们想要实现的范围。” 不过因为这是一个更广泛的社区,你可以有插件、附加组件,或者是与主项目并行的功能。我们在 Fyne 中已经开始这样做了,我们有一个扩展库,就像 Go 语言一样。这是一个很好的模型,运作得很好。所以一些可能是未来的一部分的功能,目前可以放在主项目之外。正如 Lea 所说,我们专注于核心团队愿意长期维护的功能。

Lea Anthony: 这也是我们在第 3 版中采用的方式---插件系统,让人们可以开发自己的功能。

Mat Ryer: 是的,特别是当建议通常不是疯狂的想法时。这些想法是合理的。有时候,如果你能在“用户空间”解决问题,虽然可能工作量会大一点,但有时候这就是答案……我们倾向于专注于启用这种功能,而不是把每个功能都添加到工具包中。我有一个小小的项目---你之前提到了 Testify,我很感激……

Natalie Pistunovich: 节目笔记……

Mat Ryer: 节目笔记……到现在也不需要再提了吧?我们还是提一下吧。

Lea Anthony: 什么是 Testify?开个玩笑,Mat。[笑]

Mat Ryer: Wails 用什么?

Lea Anthony: Is。

Mat Ryer: 是的,我正要……

Lea Anthony: 你听说过吗?

Mat Ryer: 是的,我正想说 Is。这个想法来自于 Testify 的迷你版本。它的目标是拥有最小的 API。Testify 的 API 很庞大,因为它希望尽可能简化测试的编写和阅读,所以我们添加了很多功能。我不知道我们是否有关于哪些 API 被使用的数据……但如果有那会很有趣。你可能可以编写一些工具,来跟踪人们使用了哪些 API。我得研究一下。

总之,Is 的想法是 Testify 的精简版,只有一个很小的 API。这个项目收到的 PR 主要是关于添加一些常见的功能,但你可以使用 is true,并在里面放入任何表达式。这样,你就能在测试代码中解决很多问题,而 Is 的 API 保持较小的规模。这通常是我们更喜欢的方式。但这也是一个艰难的对话,因为人们提出的想法和贡献通常都很棒,但有时与项目的目标不符。所以我认为你说得对,明确项目的使命以及它遵循的原则非常重要。

Andy Williams: 我完全同意这个观点,关于可扩展性的概念。如果人们可以自己构建某些东西,它可能以后会被添加进来,但至少它不会阻碍他们现在的应用程序开发。我们试图让现有的组件在一定程度上是可扩展的。我们不希望因为这个而让它们变得复杂,但你可以进行扩展。关键是,我们并没有使用原生组件,我们自己绘制所有组件。我们标准包中的所有组件都是使用与所有人都可以访问的公共 Canvas API 构建的。

所以如果你想做些不同的事情,完全可以在自己的代码中实现。这可能不会只是几行代码,因为你是在做非标准的事情,但它确实帮助了一些企业客户,他们可能会说“我们必须这样做”或者“我们必须添加这个功能。” 通过一些帮助,他们就能在自己开发的自定义组件中实现这些功能,否则他们可能会停滞不前。

Mat Ryer: 好的。现在是时候了,Natalie,你知道现在是什么时间了吗?

Natalie Pistunovich: [唱歌] 这就是它的延续……

Mat Ryer: 是的,大家准备好吧。现在是“不受欢迎的观点”时间!

Mat Ryer: 哇,真让人精疲力尽。Lea,你有什么不受欢迎的观点?

Lea Anthony: 我今天的不受欢迎观点是,最适合的工具并不总是最适合的工具。

Mat Ryer: 什么?!

Lea Anthony:那么,我的意思是什么呢……?是的,这个观点会有多不受欢迎呢……?

Mat Ryer:[笑] 是啊,说某样东西不是它本身,这肯定会非常不受欢迎,我想。

Lea Anthony:可以说是有争议的。作为工程师,我们总是试图使用最适合的工具来完成工作,因为我们想做一份出色的工作。然而,如果你是在为自己的时间付费,那没问题。但如果是别人为你的时间付费,那么你设计的工具是否是整体上最适合的工具呢?我最近通过一篇文章意识到了这一点,文章的作者我现在记不起名字了。他基本上讲的是,他有一个新来的经理,他们想要实现---我不太记得是个什么东西了,可能是一个队列之类的。于是他们就说:“哦,那我们去用 Kafka 吧”,或者类似的为这种任务设计的技术。

Mat Ryer:哦,我还以为你说的是台球杆呢。

Lea Anthony:哦,我非常喜欢台球,非常非常喜欢打台球。我们可以聊聊这个。

Mat Ryer:你游泳吗?

Lea Anthony:我游泳。我有个游泳池,所以还不错。

Mat Ryer:真的吗?你有台球桌吗?

Lea Anthony:没有,我应该买一个。

Mat Ryer:你真的有游泳池吗?

Lea Anthony:是的,我旁边还有一张桌子,但我想这不是你想问的吧?

Mat Ryer:不,我觉得你得把桌子放进池子里才算数。

Lea Anthony:好吧,我待会儿发张照片给你。

Mat Ryer:住在澳大利亚的人都有游泳池?这太疯狂了。好吧,抱歉打断你了……

Lea Anthony:没关系,我刚说到哪了?哦对了,新经理来了之后问:“你们现在用的是什么?”我想他们当时用的是 Postgres 或类似的东西。然后经理说:“对于这个用例,让我们用 Postgres 来实现吧。” 工程师们当时都觉得:“你在说什么?这太疯狂了。” 但他们还是这么做了。之所以这是个好主意,是因为这是他们完全理解的技术,他们有所有的控制手段,如果出了问题,他们可以应对。基于他们的用例,Postgres 完全足够了,甚至比他们需要的还要多。所以尽管它不是最适合的工具,但它确实是最适合的工具。这是我最近学到的一个有趣的东西。

Mat Ryer:是的,我对此非常感兴趣。我还想说,在这个例子中,除了他们熟悉 Postgres 之外,还避免了引入新的技术。所以维护和操作的东西也减少了,这也非常有价值。我认为我们经常犯的一个错误是忘记了维护的重要性,因为我们通常把它看作是开发和部署以及添加新功能的背景噪音,往往会忽略它。但实际上,尤其是对于那些成功的项目,维护的成本通常远远超过了最初的开发成本。而且,简化基础设施也是一件好事。所以我很认同这个观点。我在想,这个观点会有多不受欢迎呢?

Andy Williams:不错。是啊。

Mat Ryer:还有其他观点吗?

Andy Williams:是的,我有一个不受欢迎的观点可以分享……

Mat Ryer:哦,那请讲。

Andy Williams:我今天的这个不受欢迎的观点是---我原本不知道这会是不受欢迎的,但之前我提到过,我们在 Go、Fyne 和 Wails 等社区里有一个很棒的群体。我们非常幸运地有一些赞助商帮助我们推动项目,这和 Go Time 以及其他一些很棒的地方是一样的……不过,我一直在寻找一些方法,以便让项目能够在未来得到更多的支持,扩大它的影响力。这时我意识到,也许并不是所有人都同意我的看法。我的观点是:开发者会为了一个有用的工具包去学习一门新的编程语言。我原以为大家会选择合适的工具,并且会为了这个工具去学习相应的技术。一旦你学会了两三门编程语言,再学一门其实也不是什么大问题,尤其是像 Go 这样在语法上非常熟悉的语言。所以我非常有信心,人们会为了使用这些优秀的新图形应用技术去学习 Go。但是,似乎有些人并不这么认为,他们觉得这样做有点冒险。所以,这就是我今天的不受欢迎的观点。

Mat Ryer:我们会在我们的 Twitter 账号 @gotimeFM 上测试这些观点,然后告诉你,Andy,看看到底是不是真的不受欢迎。但这很有趣。我肯定会这么做。我肯定会为了使用某个工具去学一门新语言。事实上,我还挺喜欢学习新语言的,当然是编程语言。毕竟我是英国人,不会说其他语言。我们英国人真是傻。所以我挺喜欢学习新语言。**我有时候还会怀念技术困难的那些日子。当一件事变得简单时,我反而会失去一点动力。我喜欢那种困难时期的推动力,当你无法解决问题时,你会有那种干劲。所以我挺喜欢这种感觉的。**不过,这确实很有趣。不知道大家有什么看法?

Natalie Pistunovich:我尝试了很多编程语言,或者让 AI 编写一些东西,所以我几乎看不到尝试新东西的障碍。比如用这门语言写这个东西,或者使用这个工具包。也许做个基准测试会很有趣。[笑] 这几乎和你说的相反,Mat,你经历了学习和尝试新语言的艰难过程,而我慢慢开始看不到这种学习的好处。

Mat Ryer:是啊。我并不是说这样做有什么好处……事实上,我做的大多数事情都没有什么好处。

Natalie Pistunovich:如果是为了好玩,那当然可以,100%支持。

Mat Ryer:是啊。那么你会让 AI 帮你写代码吗?

Natalie Pistunovich:是的。

Mat Ryer:这不算作弊吗?

Natalie Pistunovich:我不确定。你在 IDE 里用过任何插件吗?你用过代码补全功能吗?

Mat Ryer:不,没有……

Natalie Pistunovich:没有?你用的是纸和笔。[笑]

Andy Williams:他是用穿孔卡片来编程的。

Mat Ryer:我会喜欢那样的。是啊,我怀念技术还很糟糕的那些日子。我怀念那些年……

Natalie Pistunovich:那就别用 Go 了。[笑]

Mat Ryer:该转向 Java 了。抱歉,Java 开发者们……不过,我想 Java 开发者不会听这个节目吧?我不知道。

Lea Anthony:可能有些前 Java 开发者会听。

Mat Ryer:嗯,他们会被冒犯吗?

Lea Anthony:可能不会,但谁知道呢。

Mat Ryer:可能不会。是啊。无论如何,Natalie,当你辅导初级工程师或刚入行的人时,你会鼓励他们学习其他语言吗?

Natalie Pistunovich:我一定会鼓励他们学习一些概念,比如设计模式。设计模式、算法这些跨越所有语言的东西你一定要知道。语言只是实现好点子或概念的工具,或者是实现可复制的东西的工具。所以那些能够跨语言的东西你应该掌握。至于具体的语法,我越来越觉得它不那么重要了。这可能不是个不受欢迎的观点,尽管它有可能是。

Mat Ryer:我们拭目以待。我不确定。不过你确实有一个不受欢迎的观点……

Natalie Pistunovich:我有另一个不受欢迎的观点。我在想我之前说的那个是否也算作一个不受欢迎的观点。我得在另一期节目里试试。不过在这一期里,我的不受欢迎观点来自我们在录制前的技术测试,当时我们讨论今天是春分日。在一些语言中,“天”这个词有不同的含义,既可以指从午夜到午夜的24小时,也可以指白天的日光时间。英语中没有这种区分。我很好奇这有多不受欢迎,但我觉得在这个方面我们错过了一些东西。我们应该有两个单独的词来表达这两个概念。

Mat Ryer:是啊,不过……有时候你可能会说,比如我问你“现在是白天还是黑夜?” 如果我这么问你,我想……

Natalie Pistunovich:我会很困惑。这是在问24小时的时间段,还是在问白天?

Mat Ryer:对,那确实是个奇怪的问题。就像问“你想要食物,还是一个汉堡?” 这挺让人迷惑的。

Andy Williams:尤其是---如果你有窗户,可以很容易地回答这个问题。不是汉堡那个,抱歉;是白天还是黑夜的问题。

Mat Ryer:你是说通过看窗外。

Andy Williams:没错。

Mat Ryer:是啊。那么,Natalie,你能举个例子,说明哪种语言有这种区分?

Natalie Pistunovich:希伯来语。

Mat Ryer:那这两个词是什么?

Natalie Pistunovich: 从午夜到午夜是 Yemama,而从早晨到傍晚是 Yom。

Mat Ryer: 哦,听起来挺酷的。不过我不觉得这对我们有什么影响。就像,我从来没有注意到---你懂我的意思吗?我从来没有---

Natalie Pistunovich: 因为在你的脑海里这两者是一样的。但我确实注意到了。我现在回忆不起具体的情况,但我记得有过一些场合,我在想,“为什么?为什么要丢掉这个概念?”

Mat Ryer: 太神奇了。

Natalie Pistunovich: 尤其是英语有那么多词汇来描述许多类似的事物。

Mat Ryer: 是啊,但我们就是没在意这个。我有一个不太受欢迎的观点。我觉得这个观点可能会让一些人生气……希望不会,但也希望会。我的不太受欢迎的观点是 Wails 的 logo,也就是 Wails 应用的 logo 是世界上最好的应用 logo。如果你还没见过这个漂亮的红色龙…… Lea,这背后有什么故事吗?

Lea Anthony: 嗯,我希望我能说这是我设计的。但我的设计水平跟两岁的孩子差不多,所以我得外包出去。最初的 logo 是我看到的一种纹身风格的设计---你知道那种纹身风格的龙吧……最初的原因是它看起来有点像亚洲风格,同时还保留了和 WAILS 龙的联系。而到了第二版,我想“你知道吗?我想要一个非常有冲击力的 logo。这是一个大版本发布,我想让它变得更大,更特别一些。”

所以我四处打听,我在 Twitter 上发帖问“有人认识设计师吗?” 结果---噢,千万别这样做。天哪,你会收到一堆乱七八糟的回复,就像你在说你有一份年薪 30 万美元的工作一样。你会收到太多的回应。其中有个人发了他的作品集给我,真的很棒。我想“这家伙可能超出我的预算了。”但我们聊了几句,来回讨论了一下,然后他就设计出了这个很棒的 logo。我当时就觉得“是的,我基本上不需要改什么了。这太有冲击力了。” 真的很棒。所以是的,这就是这个 logo 的故事。我开始使用它后,收到了一些很好的反馈。不过遗憾的是,这不是我设计的。但确实是一个很棒的作品。

Mat Ryer: 没错,真是个棒极了的作品。我们还得提一下,威尔士的国旗---它可能也是世界上最好的国旗。我不想展开这个话题---

Andy Williams: 我觉得它得到了投票认可。

Mat Ryer: 旗帜确实需要投票,所以这很好。

Andy Williams: 呃…… [笑声]

Mat Ryer: 你们都见过威尔士国旗吗?Natalie,你见过威尔士国旗吗?

Natalie Pistunovich: 我现在正在谷歌搜。

Mat Ryer: 搜一下,你不敢相信。

Natalie Pistunovich: 我可能见过,但我现在要搜一下。

Mat Ryer: 我们会在节目注释里放一个威尔士国旗的链接。你不会相信这是真实的国旗。它是真的。

Natalie Pistunovich: 我喜欢这条龙。它也是一条红龙,走在草地上,天上是一片纯白的天空。

Andy Williams: 是的,那是云。

Mat Ryer: 为什么是白色的?

Natalie Pistunovich: 那天有点多云。

Mat Ryer: 就是个晴天,对吧?

Natalie Pistunovich: 那是雾。

Mat Ryer: 水旗。好的,很遗憾我们今天的时间到此为止了。非常感谢你们。这次我们学到了很多关于 Fyne、Wails、跨平台编码、开源社区和开源项目管理的内容……Andy Williams,非常感谢你。顺便说一下,我很喜欢你制作的配乐。我知道你做了《侏罗纪公园》的配乐。那真是一首经典曲目。

Andy Williams: 太好了,简直不敢相信。你得把所有这些东西混合在一起。毕竟,创造力是软件工程的重要组成部分。

Mat Ryer: 是啊。斯皮尔伯格是什么样的人?

Andy Williams: 说实话,他没我想象的那么多时间来关注我,尽管我在新闻里读到我们关系挺近的。

Mat Ryer: 真可惜。真是太可惜了。

Andy Williams: 也许我们可以再联系一次,试着让他参与下一次 GopherCon,看看能不能一起做点什么。

Mat Ryer: 我觉得开场的视频效果会很棒,不是吗?

Andy Williams: 绝对的。实际上,我已经迫不及待想看到它了。我们赶紧搞定吧。

Mat Ryer: 完全赞同。Lea……

Lea Anthony: 嗨!

Mat Ryer: Lea Anthony。哦,这原本是个结尾部分。

Lea Anthony: 你好。

Mat Ryer: 不,别说你好了。现在是告别部分了。

Lea Anthony: 啊……但我真的不想告别。我真的玩得很开心。

Mat Ryer: 这是个好借口,因为……

Lea Anthony: 是啊。

Mat Ryer: 这段肯定会被剪进去。你说了这句话,肯定会被剪进去。

Lea Anthony: 可以把这当作我的不太受欢迎的观点吗? [笑声]

Mat Ryer: Lea Anthony 来自 Wails 和 Wails 项目,Andy Williams,当然还有 Natalie Pistunovich,非常感谢你们。下次见,Go Time 节目再会!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/890037.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ORA-65096:公用用户名或角色名无效

CREATE USER DATA_SHARING IDENTIFIED BY "Ab2"; Oracle建立用户的的时候,可能会出现一直提示 ORA-65096:公用用户名或角色名无效; 我查了一下,好像是 oracle 12版本及以上版本的特性,用户名必须加c##或者C##前缀才能创…

拆解学习【反激-PD-氮化镓】(一)

小米67W桌面快充插座: 反激基本拓扑: 商用场景下,这个拓扑进行了如下优化: 1.Q22换成了氮化镓开关管,当然需要适配的能驱动氮化镓的控制芯片 2.D21二极管换成了MOS管。 3.由于是AC220V输入,设计了整流桥…

Android Camera系列(四):TextureView+OpenGL ES+Camera

别人贪婪时我恐惧,别人恐惧时我贪婪 Android Camera系列(一):SurfaceViewCamera Android Camera系列(二):TextureViewCamera Android Camera系列(三):GLSur…

【Nginx系列】Nginx启动失败

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

轻量服务器和云服务器ecs哪个好用一些?

轻量服务器和云服务器ecs哪个好用一些?轻量服务器与云服务器ECS在多方面存在显著差异,对于需要高性能计算和大规模数据处理的用户来说,ECS可能是更好的选择;而对于预算有限且需求较为简单的用户来说,轻量服务器可能更为…

Cpp::STL—list类的模拟实现(上)(13)

文章目录 前言一、结点类的实现二、迭代器类的实现迭代器类的存在意义迭代器类的模板参数构造函数运算符的重载--运算符的重载、!运算符的重载*运算符的重载->运算符的重载 总结 前言 注意本篇难度偏高,其主要体现在迭代器类的实现!   什么&#xf…

QD1-P8 HTML 格式化标签(font、pre、b、strong、i、u、del、s、sub、sup)

本节学习&#xff1a;HTML 格式化标签。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p8 ‍ 一、font 标签 用途&#xff1a;定义文本的字体大小、颜色和 face&#xff08;字体类型&#xff09;。 示例 <!DOCTYPE html> <html><head><meta cha…

JAVA-数据结构-排序

1.直接插入排序 1.原理&#xff1a;和玩扑克牌一样&#xff0c;从左边第二个牌开始&#xff0c;选中这个&#xff0c;和前面的所有牌比较&#xff0c;插在合适的位置 public static void insertsort(int[] arr){//直接插入排序for (int i 1; i < arr.length; i) {//此循环…

LOID:有效提升遮挡条件下的车道检测精度

1.论文信息 论文标题&#xff1a;LOID: Lane Occlusion Inpainting and Detection for Enhanced Autonomous Driving Systems 作者&#xff1a;Aayush Agrawal, Ashmitha Jaysi Sivakumar, Ibrahim Kaif∗, Chayan Banerjee† 作者单位&#xff1a;印度马德拉斯印度理工学院&…

Web安全 - 路径穿越(Path Traversal)

文章目录 OWASP 2023 TOP 10导图定义路径穿越的原理常见攻击目标防御措施输入验证和清理避免直接拼接用户输入最小化权限日志监控 ExampleCode漏洞代码&#xff1a;路径穿越攻击案例漏洞说明修复后的安全代码代码分析 其他不同文件系统下的路径穿越特性Windows系统类Unix系统&a…

【C++】基于红黑树封装set和map

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、更高维度的泛型二、模版参数三、比较逻辑的重写四、迭代器4.1 const迭代器4.2 重载4.3 - -重载 五、完整代…

为什么很多人宁愿加钱买港版,也不愿买国行 iPhone 16

最近的 iPhone 16 市场&#xff0c;真的是倒反天罡&#xff0c;攻守异形啊。 过去&#xff0c;港版 iPhone 都是性价比的次选&#xff0c;便宜个 10% 都得考虑考虑。但今年&#xff0c;港版 iPhone 16 的价格&#xff0c;反而比国行还贵。 比如&#xff0c;闲鱼上某个卖家&am…

[红队apt]文件捆绑攻击流程

免责声明:本文用于了解攻击者攻击手法&#xff0c;切勿用于不法用途 前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理黑客通过文件捆绑进行攻击的流程思路 文件捆绑原理 废话只多说这一句。 1.exe和2.exe被你捆绑为3.exe。 那么你点击了3.exe就等于点…

kafka消息队列核心内容及常见问题

目录 1. 使用消息队列的目的&#xff08;优点与缺点&#xff09; 2. 常见各消息队列对比 3. kafka介绍 3.1 kafka简介 3.2 kafka特点 3.3 kafka系统架构 3.4 设置数据可靠性 3.4.1 Topic 分区副本 3.4.2 消息确认机制 4. 常见问题&#xff08;面试题&#xff09; 4.…

Acwing 排序

1.快速排序 主要思想&#xff1a;基于分治思想。通过选择一个基准元素&#xff0c;将数组分为两部分&#xff0c;左边部分元素都小于等于基准&#xff0c;右边部分元素都大于等于基准。然后对这两部分分别递归地进行排序。 分区逻辑&#xff1a;双指针算法 左指针i从左往右找…

《RabbitMQ篇》消息应答和发布确认

消息应答 消息应答机制&#xff1a;消费者接收信息并处理完之后&#xff0c;告诉rabbitmq该信息已经处理&#xff0c;rabbitmq可以把该信息删除了. 消息自动重新入队&#xff1a;如果处理某个消息的消费者异常关闭了&#xff0c;没有发送ACK确认&#xff0c;rabbitmq会将其重…

金九银十软件测试面试题(800道)

今年你的目标是拿下大厂offer&#xff1f;还是多少万年薪&#xff1f;其实这些都离不开日积月累的过程。 为此我特意整理出一份&#xff08;超详细笔记/面试题&#xff09;它几乎涵盖了所有的测试开发技术栈&#xff0c;非常珍贵&#xff0c;人手一份 肝完进大厂 妥妥的&#…

postgresql 安装

一、下载 PostgreSQL: File Browser 下载地址 PostgreSQL: File Browser 上传到服务器,并解压 二、安装依赖 yum install -y perl-ExtUtils-Embed readline-devel zlib-devel pam-devel libxml2-devel libxslt-devel openldap-devel 创建postgresql 和目录 useradd …

Java-基础

1. 导入模块不能纯粹的复制粘贴&#xff0c;要从new里导入&#xff0c;因为前者建立不了关联 2. 数组 String[] name{"张三","李四","王五"};int[] numsnew int[]{1,2,3};//二维String[][] names{{"张三","李四"},{"…

39 C 语言枚举类型、枚举常量、枚举变量、枚举的遍历、枚举数组、枚举与 switch

目录 1 什么是枚举 2 定义枚举类型 2.1 语法格式 2.2 枚举元素的特点 2.3 案例演示 3 枚举变量 3.1 什么是枚举变量 3.2 定义枚举变量的多种方式 3.3 案例演示 1&#xff1a;标准版枚举类型 3.4 案例演示 2&#xff1a;简化版枚举类型 3.5 案例演示 3&#xff1a;匿…