你正在使用的Go版本是什么(go version
)?
$ go version
go version go1.16beta1 darwin/amd64
这个问题在最新版本的发布中是否会重现?
是的,函数singleJoiningSlash没有导出。
go/src/net/http/httputil/reverseproxy.go
第102行到第112行 1004a7c
| | funcsingleJoiningSlash(a, bstring) string { |
| | aslash:=strings.HasSuffix(a, "/") |
| | bslash:=strings.HasPrefix(b, "/") |
| | switch { |
| | caseaslash&&bslash: |
| | returna+b[1:] |
| | case!aslash&&!bslash: |
| | returna+"/"+b |
| | } |
| | returna+b |
| | } |
你期望看到什么?
这个函数应该被导出,因为几个开源项目需要复制它而不是直接使用它,如果你已经将net/http/httputil作为依赖项使用,这将是不最优的:
Openshift:
https://github.com/openshift/console/blob/master/pkg/proxy/proxy.go#L69
AWS Proxy:
https://github.com/acquia/aws-proxy/blob/master/proxy/reverseproxy.go
Chronograf:
https://github.com/influxdata/influxdb/blob/master/chronograf/server/proxy.go
8条答案
按热度按时间ybzsozfc1#
/cc @bradfitz@neild
mlnl4t2r2#
这显然对我来说并不明显。复制一个小而高度专业化的函数似乎比添加一个依赖项要好得多。
biswetbf3#
感谢neild的快速回复!在所有提到的仓库(我只复制了我在谷歌上找到的前3个,似乎有很多人复制这个函数并导入那个包),这个包已经是依赖项,所以你不会引入一个新的。但我同意,我应该提到这个条件。
无论这个问题是否认为小函数的代码复制是坏的,我真的没有看到为什么让这个函数公开可访问会带来任何危害,当它对那些不想复制代码并自己维护它的人有明显的好处时。
cgvd09ve4#
每一块出口的API表面都伴随着成本,无论是打包出口它的还是每个导入它的包。这些成本在用户这边不太明显,但它们是真实的:增加的API表面使得一个包更难以使用,对依赖项的任何更改都有可能破坏依赖它的代码,存在一个用于做某事的导出函数可能会错误地暗示这是应该做的事情。
看到一个包中的内部函数,看到它的用途,然后说这个函数应该被导出,因为它是显而易见有用的,这种做法很容易让人产生误解。按照这种方法,包API会自然地增长,而不是通过设计来实现。
包API应该随着设计的发展而增长,而不仅仅是因为代码存在。
xiozqbni5#
我完全同意,导出函数总是伴随着风险,并且应该与包的设计相一致。然而,我认为在这个例子中,
net/http/httputil
包的设计非常符合该函数所做的工作。根据包本身的意图,它旨在提供HTTP实用函数,补充net/http包中更常见的函数。对我来说,函数
singleJoiningSlash
显然是一个适合这个目的的实用函数,同时也被社区明确需要并消费。它很小,高度专业化,可能永远不会改变,所以我认为暴露它的风险是可以评估的。我认为在这种情况下的最佳实践不应该仅仅是复制内部包代码,如果你已经消费了这个包。fnatzsnv6#
我同意这个功能是有用的,但我不同意“可能永远不会改变”。在几个问题中已经提到了这一点,我可以很容易地看到,如果它被导出,改变一些特殊情况(如空字符串等)可能会对包很有用,但如果这样做会带来负面影响或不可能实现。
以 #25710 为例,可以看到其中的一些隐藏复杂性。
92vpleto7#
@toothrot 我看到了,谢谢。我之前没有意识到关于它的讨论。我仍然对此感到有些不满意,因为我认为很多大型开源项目在复制和依赖这段代码(包括我自己)时,并没有了解它周围的边缘情况和隐藏的复杂性,他们都需要自己去发现这些问题。
所以虽然我同意将这个函数原封不动地导出可能是次优的选择,但在这个包中,有没有其他方法可以为这个功能提供一个经过良好文档记录且公开可访问的函数呢?
9q78igpj8#
cc @ianlancetaylor as this seems like a Proposal.