<p>Hey folks,</p>
<p>I've been struggeling a bit around with Go's export/unexport behaviour.
Maybe it's just me but sometimes I miss the ability to make fields only accessible from the inside of the owner struct and not from the whole package.</p>
<p>Please look at the following small <a href="https://gist.github.com/anonymous/6b2f347af01821a9fe0446689c733720">example</a>.</p>
<p>This code is just a small sketch of a little program from myself which should just collect hosts in a given range. This project is extreme small and because of this the whole code (except the main func) lives inside one package.</p>
<p>To avoid race conditions you have to use collector.FeedIPRange because Run() usually does not run in the main goroutine.</p>
<p>The problem is how do I enforce myself and others not to add to collector.ipRanges directly and instead use FeedIPRange.
I could move the collector to another package or I could create a interface for it but the simplest solution I could imagine would be the ability to make this field just accessible from inside of the struct.</p>
<p>My simplest and first thoughts were just a possibility to declare a struct field as "struct level private". Something like this:</p>
<pre><code>type collector struct{
//I'am just accessible from inside of collector because of the leading _
_ipAddrs []string
}
</code></pre>
<p>What do you think? I am just overthinking it?</p>
<hr/>**评论:**<br/><br/>faiface: <pre><p>The way to deal with this is to simply not access the field. This discipline will only apply to you, because users of your package won't see the unexported field. Accidentaly accessing a "private field" of another struct just doesn't happen.</p></pre>st0n1e: <pre><p>I'm afraid of accidentally using it after a few months of not working at this project. With "others" I mean people which will probably modify my code. A colleague of me (sysadmin like me) which is closely working with me at our infrastructure is starting to get his hands dirty with Go. My fear is when he gets starting to work on one of my projects, that he does not read the docs well enough.</p>
<p>I think I stick with the solution from <a href="/u/elsyms">/u/elsyms</a>. Seems reliable to me, but I was hoping for a simpler approach like just change the first char of the field :-). </p></pre>faiface: <pre><p>I would not recommend that as that leads to too many unnecessary interfaces. I would just solve the problem when it actually appears and I believe it won't really appear.</p></pre>st0n1e: <pre><p>That is my fear as well. But for this small project it just does the job. I would prefer something like I suggested in bigger projects. Just to have the compiler to watch my back in situations like this.</p></pre>dlsspy: <pre><p>Fear isn't a good signal for making design decisions.</p></pre>jerf: <pre><p>If you're that worried, which is fine, put it in it's own package. I end up with a lot of small packages. My co-workers have said that combined with the godoc this seems to be easy to understand.</p>
<p>Also bear in mind that even if you do that, anybody can come along later and move it back anyhow, or capitalize the field later. It's not like you have any options other than "strong suggestion" anyhow.</p></pre>Vhin: <pre><p>If it's functionality that you feel that you're particularly likely to want to use outside of the class, maybe you should be exporting it. If it's not, then I wouldn't worry about it.</p>
<p>Even if someone else does start using a method you considered private, it's still internal to the package, and shouldn't be difficult to change later.</p>
<p>Also, even if there were access level specifiers, if you're worried about people who have access to the source code of your package, they could just broaden the access level specifier and use it anyway.</p></pre>__crackers__: <pre><blockquote>
<p>I was hoping for a simpler approach</p>
</blockquote>
<p>You could try trusting your colleague.</p></pre>st0n1e: <pre><p>But why not try to make things more clear or tough against false use.
I like to tackle possible bugs before they occur :-)</p></pre>__crackers__: <pre><p>Because it requires hoop-jumping and adding complexity.</p>
<p>I don't know your colleague. Perhaps he really can't be trusted to read and follow the docs. But it sounds a lot like premature optimisation to me.</p></pre>Femaref: <pre><p>If the field is unexported, users from outside the package can't access it anyway. </p>
<p>I've had the same situation as well, it turned it out it didn't matter in practise. The only time you access the unexported field in a method not belonging to the struct is in a <code>New</code> function. I think you are overthinking it.</p></pre>st0n1e: <pre><p>Thank you for answer. I was talking about access from inside of the package :-). For example method "a" in the same package cannot access field "c" of struct "b" because field "c" should be only available to the "b" struct himself.</p>
<p>I tried to make a suggestion to improve Go. Look at <a href="/u/elsyms" rel="nofollow">/u/elsyms</a> answer. This is exactly what I need but I want it simpler.</p></pre>drvd: <pre><p>Inside the package is like inside a method. Inside a package you should know what you do and unit test it.</p>
<p>Get used to it. Is the best advice I can give. There is nothing to improve or change here.</p></pre>xargon7: <pre><p>If you're worried that you might forget about the restrictions on collector when further developing this package in a few months, then just formalize collector into it's own package. That way you are forced to use it via the API it provides. Put this <code>collector</code> package as an internal subpackage of the package you are developing. Voila, you are now safe.</p></pre>st0n1e: <pre><p>That makes sense. Good argument.</p></pre>p7r: <pre><p>The fact that <code>ipRanges</code> is not exported (it starts lower-case), to my eye clearly marks it out as a private field within the struct that I should deal with very carefully.</p>
<p>If you want to make it only available inside a <code>collector</code>, separate that out into a separate package if you must, but YAGNI. Probably.</p>
<p>If somebody decides to add a function to that package that uses <code>ipRanges</code>, they probably did so for a reason you have not yet anticipated.</p>
<p>At that point, you either refactor, or you realise you were over-designing by preventing such access right now.</p>
<p>I'd say all is well, but if you're being paranoid, pull it into a separate package.</p></pre>st0n1e: <pre><p>Yeah that's true. Maybe I just didn't think simple enough like the Go idiom even after 3 years of Go (I'm not a full time programmer). I started programming with C#. Maybe some leftovers from the past.</p></pre>p7r: <pre><p>The day you stop learning as a dev, that's the day you know your career is over. :-)</p></pre>ant-ham: <pre><p>You have to move it in its own package as it was said before and in an internal package if you don't want it to be available outside. If you start to think you need to "hide" some elements of your implementation it means you have a public and private part so you need to move your code in a package. I guess we make a big deal of that but it is not, creating a module is nothing especially if you scope it as an internal package, no one outside will notice about it and you make a clear statement that the code in your internal package works as its own.</p></pre>nagai: <pre><p>Turns out programmers are perfectly capable of managing internal state within their own package without private/protected/public directives. Who would've thought..</p></pre>st0n1e: <pre><p>Yeah I'm capable of this as well but I prefer to enforce things like this.
It can not hurt when the compiler has my back in such situations.
This was just a simple example and in bigger packages things can get more complicated. I'm just a human and humans do errors. Who would've thought...</p></pre>breamoreboy: <pre><p>Or they can do the complete opposite by such clever things as:-</p>
<pre><code>#define public private
#define private public
</code></pre></pre>TheMerovius: <pre><blockquote>
<p>The problem is how do I enforce myself and others not to add to collector.ipRanges directly and instead use FeedIPRange</p>
</blockquote>
<p>Personally, my policy is to never access unexported fields outside of methods of the type itself (and maybe tests); even if I can.</p>
<p>There are exceptions to this, but as long as this is my default policy, I am always <em>intentional</em> about doing it if there is an exception. Like, I think "why am I accessing this unexported field? ah, right, it's okay here, because…", preventing problems.</p></pre>elsyms: <pre><p>Expose an interface that has the method you need from whatever method instantiates your collector struct. This will then hide behaviour that is not declared within the interface definition.</p></pre>st0n1e: <pre><p>Thank you. I think I stick with this solution. Had this already in my mind but I was hoping for something simpler like just modifying the first char of the field like in my approach. But life isn't always a bowl of cherries. :-)</p></pre>: <pre><p>[deleted]</p></pre>st0n1e: <pre><p>Thanks for you answer. I know how to export/unexport. My intention was to hide fields of a struct from other funcs inside of the same package.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传