V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  kuanat  ›  全部回复第 1 页 / 共 7 页
回复总数  140
1  2  3  4  5  6  7  
2 小时 36 分钟前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@AbrahamKindle #38

我不太确定你所说的性能指的是哪个方面,我就随便说一下。

原文里谈到 Go 不适合音频视频处理指的是生态问题。比如图片领域有 imagemagick 音频视频领域有 ffmpeg ,通常没有重新造轮子的。如果要用到 Go 项目里就需要通过 FFI 来调用,这就需要通过 CGO 的方式。如果没有人开发相应的 binding ,那就要通过命令行直接调用了。这两个都不是很理想的方式。

音视频处理领域谈性能,一般应该是实时领域吧,指标是延迟和 jitter 这种。粗略来说,不仅仅是 Go ,凡是带 GC 的语言都不适合干这个事情,因为没有完全控制 GC 介入行为的方法,GC 介入会短暂中断程序的运行,这对实时应用来说很难接受。当然这个事情不绝对,通过一些手动管理对象、内存或者 zero copy 流式处理的方式说不定也能满足性能需求,就是回到 C 那种思路上面。

另外音频视频处理涉及的范围很广,以我有限的经验来说:

- 涉及底层硬件的尽量还是用 C ,写 binding 一点都不好玩
- 调用个摄像头做 cv 识别这种是可以的,因为你对接的不是硬件而是 v4l2 之类的抽象,opencv 的 golang binding 也够用
- 走 opengl 写个 shader 什么的也还好,但是基本没法对接到一般游戏开发工作流里面

所以说还是尽量用对应领域的工具吧。
抱着解答问题的心态点进来,看见楼上的评论就笑了。名著你可以揣测一下作者的写作思路,地摊文学就算了……

代码层面的问题 @bv 在楼上已经说得比较清楚了,我补充一点我的理解。

这种代码水平在我之前的面试标准里面,按照初级/中级/高级三档水平,只能划分到初级那一档。我只通过看链接里那个文件就做出了判断,而且对此我非常有把握。

划分到初级的主要理由是:无法准确使用接口 Interface 来实现功能解耦。这个能力在我之前负责面试的时候是中级技能里的。相比之下,代码层面反倒是小问题了。

如果看不懂的话,我可以抽时间单独就 Go 通过接口来解耦专门做个解释。有隔壁那个代码氛围的帖子在,其实不想过多评价。但我还是要说,这段代码的作者的 Go 水平真就是初学者,出来卖课就是误人子弟了。
2 天前
回复了 zoeyx 创建的主题 程序员 前端 Coder 如何学习 Golang?
语法层面过一遍官方的 Tour 差不多够了。

练手的话我比较推荐尝试写个爬虫服务,不是让你真去爬什么,而是这个过程用到的东西学习曲线比较平滑。

解析页面元素这个过程,大概能熟悉强类型语言处理字符串的模式,了解结构体的应用。后期还会接触到反射等机制的应用。

之后是一般的网络编程,发送接收请求。这个过程可以熟悉标准库的风格和惯例,网络库算是 Go 比较精髓的部分了。

再之后是多线程处理,了解 chan/goroutine 的使用,以及常见的并发模型。领会一下用通信的方式来共享内存的核心思维。

最后把改造成服务,学习一下路由处理、中间件等等服务端常见的应用。之后有可能会慢慢接触到模板、泛型的应用。

前期不用考虑处理太复杂的情形,用标准库把功能实现出来就行。整个过程里可以慢慢熟悉后端工程化的实践。
2 天前
回复了 saranz 创建的主题 V2EX 好奇,至今还在 V2EX 活跃的 ID 是那些。
一般差 20 岁就是一代人,V2EX 从 10 算已经 15 年多,当年的种子用户可能都已经三十多岁了。

我估计有一批人应该和我一样,只是老账号各种原因不用了但是人还在。
2 天前
回复了 nobject 创建的主题 Go 编程语言 golang 日志记录
我不确定我的做法是不是 idiomatic 只是简单描述一下我的思路。毕竟 slog 进标准库也没多久,看 api 也是受 zap 影响很大。

大的逻辑方面我比较倾向于全局只有一个唯一 logger ,中间调用过程只传递。另外我习惯把业务层面的日志和功能实现层面的日志分开,然后两者用不同的方式来处理。

业务层面的日志我倾向于用格式化的字符串:一方面它是给运维用的,需要一眼看明白在哪个环节出了问题,但并不需要知道出问题的细节;另一方面这类日志要进日志服务器做后续统计等功能,需要方便处理。

对应的就是你的描述里 controller->service->repo 这个部分,我会用 errors 传递字符串,每个环节用 errorf 来 wrap 上一个环节的错误,起到简单的 stack trace 的功能。这里一般不建议用 runtime 来获取完整的 stack trace ,主要是性能开销的问题。

包或者实现层面的日志,一般是给开发者方便调试用的,比如看到底是什么入参触发了什么样的边界条件导致出错。这个信息在开发和测试环节比较有用,实际线上是相对冗余的信息。

日志记录这个信息我用的是自定义的 context ,因为标准库里的 context 实际上只有下文没有上文。可以看这个帖子 https://s.v2ex.com/t/1012453 我的回复。

用 context 的话可以自定义数据结构,能够比较好处理那些复杂的结构体,也就是你说的第二条里的麻烦。

对于 context 信息的记录就没有必要走业务层面的 logger 了。对于不太敏感又注重性能的业务,我的处理是加一个开关,当线上出现的问题无法复现的时候,开启开关再记录完整的 runtime stack trace 信息。对于相对敏感的业务,会单独用一个 logger 导出到日志服务器单独的表中,使用 traceId 对业务层面的日志和实现层面的日志做个关联。业务层面的日志要保留很久,但功能层面的日志一般三个月就可以清理了。
2 天前
回复了 afxcn 创建的主题 Go 编程语言 使用 go 遇到的一个奇怪问题,求教
对于 rand.NewSource 存在竞态导致 panic 这一点应该没有异议,而 panic 没有中断而是正常被调用,说明上层应该是存在 recover 逻辑的。结合 OP #23 指出本地正常而测试服务器异常,推测本地测试的生成逻辑,而服务器完成的是全部调用逻辑。

@R18 #16 指出 byte 空值转换为 string 之后是空字符串,说明在可见代码的部分,只可能产生空字符串。 @AceGo 和 @hopingtop 判断出是上层调用将空字符串变成了全 0 字符串。

我这里做一点补充,一般要保证字符串定长都会做 padding ,用字符串 0 做填充是最常见的。
3 天前
回复了 Livid 创建的主题 V2EX 站点状态 20240505 - 邀请码系统
我觉得金币机制的数值设计可能需要调整一下,至于如何调整我没什么思路,这个调整的影响并不是单一的。

我这个号是特意注册的,主要是回复解答问题,算是内容质量相对较高的了吧(自认为)。简单统计了一下,不到一年时间:

- 长回复(超过 5 消耗)大概 40 次,总消耗 650 左右
- 收到感谢大约 250 次,获取 2600 左右

目前余额 8500 ,其中来自于高质量内容获取(感谢)净值只有 2000 ,其他均为自动签到或者活跃度获取的。

相对来说,目前的数值设计对于活跃度的鼓励远高于内容产出。
5 天前
回复了 0312birdzhang 创建的主题 奇思妙想 关于打包箱子引发的思考
可以参考这个装箱问题 wiki 上面 Bin_packing_problem

放到现实世界里,搬家这个场景一般都是一个固定大箱子,往里面放各种小箱子的问题。可能还会有更多约束,比如重量,一箱子书换谁都搬不动。所以人靠直觉一般能很快找到一个可行但不一定最优的方案,换作程序里可能就是经验或者启发式算法。

另外大箱子也是可以变形的,比如 50cm 长 30cm 宽,切割一下胶带一封就可以变成 40x40 或者 60x20 来用。
5 天前
回复了 unclemcz 创建的主题 Ubuntu snap 已经在污染 apt
如果你在用 Ubuntu ,同时你又不认可 Canonical 的做法,我建议你考虑一下 Debian ,它是 Ubuntu 的上游发行版。

Debian 的全称是 Debian GNU/Linux ,如果不清楚什么是 GNU 可以看官方 FAQ https://www.debian.org/doc/manuals/debian-faq/basic-defs.en.html#whatisdebian

至于原因,我这里引用一下 Richard Stallman 于 1985 年写的 GNU Manifesto 中的一段话 https://www.gnu.org/gnu/manifesto.html

- Why All Computer Users Will Benefit
Users will no longer be at the mercy of one programmer or company which owns the sources and is in sole position to make changes.
5 天前
回复了 afxcn 创建的主题 Go 编程语言 golang 的 defer 真是个好设计
@lolizeppelin #77

如果只是单纯辩论美丑,我原本没打算回复,但既然你提到了 python 用装饰器来减少代码,那我觉得还是可以讨论一下的。

生命周期管理这个事情的困难在于:资源的分配和释放可能时机不确定,又或者在代码中的位置不同,还可能是多线程的主体不确定。原地释放几乎没有人会写错,而 scope 变得很大的时候,错写或者漏写的几率就会变大。

考虑 with...as 加入到 python 中已经有二十年了,但是装饰器版本的 contextmanager 也就十年,异步安全的版本可能最近才实现。这里有个很现实的问题,作为使用者是更愿意手写 enter/exit 还是愿意手动管理?当 contextmanager 加入的时候,项目允许升级吗?当有异步、多线程需求的时候,开发者能写出正确的版本吗?

所以抛开美丑的主观看法不谈,defer 这种万金油几乎是个 Go 开发者就会写,而 with...as 使用率就要大打折扣。先让人用起来显然是重要地多的事情。这里不抬杠开发者水平高低的问题,defer 是一种兜底机制,当开发者不确定我应该在什么时候释放资源的时候就可以用,而 with...as 即便是装饰器的版本显得繁琐,写好几行肯定比不上写一个单词,这是人性。



之前的回复里我总是反复提,不要单独拿某个关键词来讨论特性,那就以 with...as 来彻底完整做个说明,证明一下为什么这样做不合适。

C/Python/Java 由于诞生较早,都是先有的实现,然后反过来总结出来的标准。比较新的语言,都是设计先于实现,终归会从之前语言中总结经验教训,所以不仅仅是 Go ,你会发现 C#/Rust/Kotlin 这些“现代”语言往往有些共性,共享的是新的设计理念。



这里 defer/with...as 反映的其实是对异常处理这件事的哲学思考不同。

传统语言中,一方面异常是不区分错误和 Panic 的,另一方面异常又与控制流强耦合,try...catch 实质是隐式的 goto 指令。

现代语言几乎都放弃了这样的思路,Panic 要立即终止,而错误是可以处理的。代码层面也尽量避免隐式跳转。这样的改变是大趋势,原因是大家都从 C 语言中汲取了足够的教训,隐式控制流是有害的( hidden control flow considered harmful ),特别是与 C 的宏机制一结合,人脑 debug 几乎变得不可能。

反过来,显式控制流的优点是显而易见的,一方面极大提高了代码的可读性以及可靠性,所见即所得,不存在那些写在其他地方的代码被意外调用执行的情况;另一方面对于并行编程的支持变得更加容易。

如果拿 Go 来举例可能没有说服力,我简单说下 Rust 和 Kotlin 。

- Rust 没有 exception ,只有 recoverable/unrecoverable 错误,类型分别为 Result<T, E> 和 panic! 只有后者是宏实现的跳转

- Kotlin 保留了 try 关键词,但 try 是表达式而非(控制流)语句,也就是说 try 可以返回(错误)值

在减轻开发者心智负担方面,新的设计哲学可谓是殊途同归。这个进步主要体现在工程化层面,当语言不区分错误和 Panic 的时候,开发者会主动选择最无脑的方式,也就是随意抛出集中处理。现代语言中砍掉了这样的机制,反过来倒逼开发者思考异常处理的本质,从而间接提高代码的可复用性,以及程序的可靠性。



回到 with...as 的问题上,Python 的错误处理还是传统的控制流模式。换句话说,就算语法糖写出花来,它的核心仍旧是基于 enter/exit 的对于 RAII 的模仿。由于 Python 没有 RAII ,这种模仿的结果就是一定要手动处理 scope 的问题。

先说一个比较接近最终方案的提案版本:

with EXPR:
____BLOCK

这个版本看上去相当美好,可是没多久就被枪毙了。原因有两个,一是 with 只能是 statement 而不能是表达式(需要同时支持 with VAR = EXPR: 这样的语法,VAR = EXPR 赋值本身也是合法的 EXPR );二是 EXPR 自身有可能再次抛出异常,或者包含 break/continue 等其他控制流指令,结果会导致 scope 失效。

第二条麻烦一下解释器也不是不可以,多维护一个基于栈的 trackback 机制。但第一条的影响是致命的:

with VAR = EXPR:
____BLOCK

想要对以上形式写装饰器,__exit__ 一定要在 VAR 上有定义,这就失去了装饰器省代码的意义。

为了不让 VAR 获得 EXPR 的赋值,最终实现的版本用 with EXPR [as VAR]: 这样的形式。相关实现细节 PEP 都能搜到,这里就不继续展开了。

即便是用上装饰器,代码大概是这样的:

@contextmanager
def open(file):
____f = open(file)
____try:
________yield f
____finally:
____f.close()

写这么多只是为了实现:可以在 with scope 里,不用单独写 f.close()。

装饰器的实现晚于 with...as 很久才进标准库,核心原因就是前面提到的隐式控制流。装饰器本身

客观地说,Python 本来就是试错型快速迭代偏好的语言,with...as 加上迭代器的语法糖的作用非常有限。它起到的主要作用是,当你知道什么时候该写 f.close() 的时候( scope 边界清晰),不用你再写了(自定义类型还是要写装饰器)。但是解决不了辅助开发者判断,什么时候该写的问题,你无法依赖解释器帮你兜底。



尽管在站队好或者不好的事情上,网友可能会支持你也可能会反对你,但不代表大家都是出于相同的推理逻辑。在语言设计这么关键的问题上,一半是妥协一半是取舍,单拿出来一个点做比较最终只会变成屁股之争。
6 天前
回复了 afxcn 创建的主题 Go 编程语言 golang 的 defer 真是个好设计
@lolizeppelin #75

确实我说 Go 好不好对 Go 和其他编程语言没有什么意义,但不代表我在这里的回帖没有意义。

很多人觉得我是狂热的吹捧者,我想还是有所误解。这里面有我表述方式的原因,也有读者立场的原因。我在之前的某个帖子里表达过,我大多数回复的目的是“创造价值”,因为我从 V2EX 学到了很多,我也想把我学到的反馈给社区。Go 的主题比较多更多是因为它有一定流行度却又不那么流行,在我日常工作里,主流的语言应用比例里 Go 反倒没有那么高。

关于 python 的 with 语法,我有个问题,你怎么看自定义对象需要手写 __enter__ __exit__ 的?

单独拿 with 出来,配合 python 的缩进,起到了看上去很美的效果。但是没有 RAII ,为了实现 scope ,又要回到手写析构的路上。

因此我一直都强调,单独拿一个关键词一个特性出来比较是不合适的。
6 天前
回复了 KinsleyNg 创建的主题 Linux 这个 Linux 的 chrome 硬解就这么难处理吗
@KinsleyNg #9

我是说不推荐 fedora ,ubuntu 什么情况不清楚。非官方打包的只能叫 chromium ,试一下官方源里的吧。
7 天前
回复了 KinsleyNg 创建的主题 Linux 这个 Linux 的 chrome 硬解就这么难处理吗
@KinsleyNg #7

实测 xwayland 有效,wayland 无效。楼上说 24.1 相关补丁才合并,目前 f40/rawhide 都只有 24.0.6 的包。

即便补丁合并了,我还是不建议以 chrome 为主力的时候选择 fedora 。由于版权的缘故,RH 系只能把解码器托管在 rpmfusion ,然后 chromium build 和原版经常不一致。反正 gtk4 在我印象里是没有生效过的。

然后 chromium 只实现了 text-input-v1 协议,这个协议除了 KDE/KWin 根本没人支持,就连 KWin 的支持还是 fcitx5 作者加回去的。所以在 Fedora 上既要 wayland 又想输入法正常,只能是 KDE 版本。而其他发行版可以 gnome/gtk4 曲线救国。

另外我只是用 fedora 但是不用 gnome ,日常主力是 sway ,sway 不支持 text-input-v1 。hyprland 那边是支持的,fedora/hyprland 这个组合可以考虑。

至于 firefox 虽然我常年支持,但是在性能方面弱不少,硬件解码支持大家都一样,但其他方面差距很大。我在另一个帖子里刚好提到了,B 站现在那个弹幕功能,firefox 的硬件加速比较原始,纯 3D overlay 。Chrome 就好很多,把 css3 用 video enhance 单元来加速。
@mayli #25

现代操作系统的显示逻辑是 C/S 架构的,应用程序也就是客户端会申请一块显示区域作为 client area 用于显示(相对应的 host area 一般是标题栏、边框这些)。

没有硬件加速的情况下,客户端区域就是各种界面库 API 或者文字 API 绘制,直接把绘制结果以像素的形式填充到客户端区域。操作系统的窗口合成器( compositor )把桌面上所有应用的显示区域做叠加合成,交给显卡完成最终显示。

有硬件加速的情况下,比如窗口中部分区域播放视频,客户端依旧会声明一块显示区域,但中间挖出一部分,并表示这部分不由客户端负责。窗口合成器也会忽略这一部分(但是还会判断遮挡关系),此时显卡的 framebuffer 就是两部分,一部分是窗口合成器给的去掉视频区域的显示数据,另一部分是视频解码单元给的视频数据。

上个回复里说 3D 其实不准确,因为所有最终的渲染都是走的 3D 单元。在显卡看来,3D 单元就是渲染单元。这个语境下的 2D 一般是指无加速,就是客户端把结果绘制完了,合成器直接拿像素数据,而 3D 有加速就是绘制 API 直接在显示区域上进行操作。这里细节我不是很确定,但大致思路就是浏览器把所有绘制 API 后端都用 3D 指令来实现了,整个浏览器内部就是一个巨大的 3D 显示,skia 文字渲染引擎也是 3D 指令在页面上显示文字。

我上个回复里描述的 3D 渲染行为,是根据风扇狂转推测的浏览器的行为。因为我是 Linux 用户,所以对硬件加速感知很深。每个浏览器都有自己如何用显卡硬件单元完成加速的实现逻辑,最终效率和性能也差别很大。目前 bili 的弹幕功能是基于 css3 的,之前有过基于 canvas 的版本但可能是因为效率问题弃用了。

目前 Firefox/Chrome/Chromium 三个浏览器的表现都是不一样的:

- Firefox 就是我上个帖子里说的模式,会调用视频解码单元,但对于 css3 的加速就很弱。从显卡资源占用上说,比较大可能就是没有优化,直接在透明层上叠加。

- Chrome 官方打包版本对于硬件加速支持很不稳定,有些版本正常有些版本就没了。

- Chromium 主要看发行版适配,官方源会打针对自己发行版的补丁。在解码器和补丁都正常的情况下,效果是最好的。首先视频本身还会走硬件解码,但是 css3 特效并非 3D 透明层,而是调用 video enhance 硬件单元。这个硬件单元的作用原本是用作视频播放去噪点、锐化等功能的,Chromium 的实现里 css3 相当于实时调整原视频,所以大幅降低了 3D 渲染单元的负载。
8 天前
回复了 KinsleyNg 创建的主题 Linux 这个 Linux 的 chrome 硬解就这么难处理吗
@KinsleyNg #5

我刚刚又确认了一下,Google 官方打包的那个版本,确实是没有视频解码硬件加速的。原因不清楚,我是拿 bili 的 av1 做的测试。

我用的 Fedora 官方源的 chromium 测试,是有视频解码硬件加速的。
8 天前
回复了 afxcn 创建的主题 Go 编程语言 golang 的 defer 真是个好设计
@Jirajine #62

我的观点一直都是,在合适的场景使用恰当的工具。在说 Go 好的帖子里这样说,在说 Go 不好的帖子里也这样说。就连这个帖子里,我的观点也是一致的,defer 不能滥用,而 error 处理要换思路。

关于 sum type 我从来没谈过,因为我想不出这样做的原因,我也没有从 Go 的官方文章找到头绪。Null safety/checked initialization 都是用来佐证,语言特性是取舍这一观点的。既然选择了简洁,就要付出代价,Go 没有特别多的选择。

既然你能准确使用这些术语,说明对这些话题有足够的认知,那完全可以阐述你对于语言设计的认识和观点,没有必要归结于个人偏好。
@sakujo #13

不开弹幕的话,就是单纯的扣一块区域,调用硬件解码器。浏览器本身是不负责播放区域的,系统窗口合成器把浏览器渲染的其他部分与视频解码的播放部分进行叠加。这个过程里,显卡的 Render/3D 单元的占用取决于视频是否在播放。暂停的话占用率会立即下降。

如果开启弹幕,弹幕区域等价于在跑一个 3D 应用,这个应用就是在播放区域之上渲染一个透明层,然后把弹幕显示上去,一般这个 3D 应用的帧率会和屏幕刷新率一致。即使视频暂停,这个透明层依旧不会停止运行。(视频暂停会看到旧弹幕飘过,只是不加载新弹幕了。)

CPU 要负责生成弹幕的帧,同时一些类似于避开视频中的人物会加大计算量,因为这个功能实际上要抓取画面,然后计算分析生成绕开人物的蒙版。

风扇转是因为 cpu gpu 都在高负荷工作。手机这个功耗水平都能完成的事情,只要硬件解码正常工作,没有理由电脑还很吃力。
8 天前
回复了 t41372 创建的主题 操作系统 很讨厌桌面端系统的权限管理模式
追加提一句作为我 #9 回复的补充。

systemd 提供了一个 sudo 的替代品叫 run0 ,今天刚发布,某种成都上是解决 sudo 是为传统多用户设计,不适合现代个人 PC 场景问题的新思路。(当然要实现沙盒化的权限机制,软件适配还是无法避免的。)

技术上 sudo 是 SUID 实现的,除去安全问题之外(任何人都可以执行 SUID 应用,所以额外需要 sudoers 配置文件),这套权限体系是不区分人和应用程序的。这就造成人控制硬件设备还要 sudo 非常麻烦,而应用获得权限反倒可以为所欲为的现状。

run0 不依赖 SUID ,而是通过 PID 1 来 fork 出一个隔离的 pty 执行相关命令和应用,权限部分由 polkit 控制。

好处就是大部分之前依赖 sudo 的硬件控制功能,都可以在已登录用户 session 的情况下无感授权。尽管还做不到像手机上那样基于隐私的权限体系,但已经可以把相对危险的操作隔离开了。

只是目前看它还是 systemd 的一部分,想要完全替代 sudo 还需要时间来验证。
9 天前
回复了 afxcn 创建的主题 Go 编程语言 golang 的 defer 真是个好设计
我看到楼上有人提多返回值的类型问题,这里也多解释一下。当然我觉得有必要再重复一次:

- 不要拿 Java/C++ 的思路去套 Go 的实现
- 不要单独讨论一个特性点的好坏

异常处理并不是只有 try...catch 这一种范式。Go 之所以设计成这个样子就是为了干掉 try...catch 。Go 这样做单独和其他语言放到一起比显得很笨,但却是非常符合 Go 需求的方式。


现实世界里“逐级汇报”和“越级处理”都是普遍存在的。习惯了随意抛出,集中处理,要是异常的类型不确定,肯定会让人抓狂,而且还会觉得,有事说事,明明没问题还汇报个屁。这样想下来,肯定觉得 Go 哪哪都是毛病。

现在换一种眼光,如果用逐级汇报的想法。Go 就明说了,我的机制比较死板,只能逐级汇报,而且模式都是没问题就过,有问题就汇报问题。这样能处理的原地就处理了,不能处理的考虑继续交给上级,直到有人能处理为止。

对 Go 来说还有个问题,要是不止一级出问题怎么办?总不能无限追加返回值的数量吧。为了解决这个问题,Go 把 error 定义成了接口:type error interface { Error() string }。

这里有两层用意,一是中间层级可以追加或修改错误内容,二是指明了 Errors are values 。第一点映射到现实就是,如果中间层级也出错,那就追加进去,甚至可以做请示备注。(这里不抬杠,C++/Java 也可以逐级处理)


Go 真正创新的地方在与第二点:错误是个值,而不是个类型。一旦明白错误是个值,你就会发现异常处理不再强依赖 goto 类控制流程。同时错误状态和错误本身也完成了解耦,控制逻辑只需要判断有没有错误,至于错误,是否处理、由谁处理甚至什么时候处理都有完全的自由。

所有纠结 Go 返回值错误类型的,有没有思考过,凭什么错误一定要是一个类型,这种先入为主的概念是从哪里来的?这就是我一直反复强调的,千万不要手里拿把锤子,就看什么都是钉子。
1  2  3  4  5  6  7  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3443 人在线   最高记录 6543   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 31ms · UTC 11:41 · PVG 19:41 · LAX 04:41 · JFK 07:41
Developed with CodeLauncher
♥ Do have faith in what you're doing.