<p>Sorry if a noob question. I just started learning Go yesterday, went through go tour, a bunch of blog posts, some example repos; and was feeling comfortable giving it a shot. </p>
<p>I am trying to read a file of email addresses, and return a channel from a function of <code>mail.Address</code> type. I can't understand why it blocks without any output or <code>deadlock</code> failure. Here is the code</p>
<pre><code>package main
import (
"bufio"
"fmt"
"net/mail"
"os"
)
const addressListFile = "./emails.txt"
func GetAddressChan(path string) <-chan *mail.Address {
addrFile, err := os.Open(path)
defer addrFile.Close()
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(addrFile)
if err := scanner.Err(); err != nil {
panic(err)
}
addrCh := make(chan *mail.Address)
for scanner.Scan() {
addr, err := mail.ParseAddress(scanner.Text())
if err != nil {
panic(err)
}
addrCh <- addr
}
close(addrCh)
return addrCh
}
func main() {
addrCh := GetAddressChan(addressListFile)
go func() {
for addr := range addrCh {
fmt.Println(addr)
}
}()
}
</code></pre>
<p>It works if instead of return a channel from <code>GetAddressChan</code>, I pass it one as an argument and run <code>GetAddressChan</code> as a goroutine.</p>
<p>Any comments on how I am not writing idiomatic Go are welcome as well.</p>
<p>Edit: I've also tried putting the goroutine inside <code>GetAddressChan</code> instead of <code>main</code> as well, in which case the program exits without printing anything:</p>
<pre><code>addrCh := make(chan *mail.Address)
go func() {
for scanner.Scan() {
addr, err := mail.ParseAddress(scanner.Text())
if err != nil {
panic(err)
}
addrCh <- addr
}
close(addrCh)
}()
return addrCh
</code></pre>
<p>Update: It is <code>bufio.Scan</code> that is causing this issue I think. It works as expected if I use a normal slice/array of strings instead of reading using <code>bufio.Scanner</code></p>
<hr/>**评论:**<br/><br/>Sythe2o0: <pre><p><code>addrCh <- addr</code> blocks until something takes the thing its trying to send, which can't happen if the goroutine at the end of main hasn't spawned yet. You could reorganize it so that <code>GetAddressChan</code> had its second half after a <code>go</code> keyword, or you could do what you suggested and pass in the channel to use.</p>
<p>As far as idiomatic go goes, you probably already know that <code>panic</code> is generally to be avoided. It'd be more idiomatic if <code>GetAddressChan</code> returned a channel and an error.</p></pre>vehlad_durjan: <pre><p>Thank your for your note.</p>
<p>I don't want to have to pass the function a channel. I want it to return a channel so I can treat it as a generator. I have tried restructuring the code so the generator send part is in a goroutine, but even then it doesn't work.</p>
<p>Please check the update to the post.</p></pre>Sythe2o0: <pre><p>Any goroutines left running when the end of <code>main</code> is reached will stop execution. Adding in some blocking operation at the end of <code>main</code> (or in this case, not putting the end of main after <code>go</code>) will prevent this.</p></pre>vehlad_durjan: <pre><p>It doesn't seem to make a difference if I block the execution after <code>go</code> statement in <code>main</code>:</p>
<pre><code>ch := make(chan int)
_ = <-ch
</code></pre>
<p>Only thing that seem to work is if I don't use any goroutine in either <code>main</code> or <code>GetAddressChan</code>, but make <code>addrCh</code> a buffered channel with buffer > number of email addresses in the file. (or if I block main from exiting <strong>and</strong> use said buffered channel)</p></pre>Sythe2o0: <pre><p><del>Because your range over addrCh will end as soon as there's nothing left there, which will be immediately, because the goroutine sending things to the channel hasn't spawned yet.</del> <- this is wrong, sorry I rarely range over channels myself</p>
<p>Then I'm not sure, can you make a link to a <a href="https://play.golang.org/" rel="nofollow">https://play.golang.org/</a> snippet with the updated code?</p>
<p>See this: <a href="https://play.golang.org/p/epTpwuycD0" rel="nofollow">https://play.golang.org/p/epTpwuycD0</a></p></pre>vehlad_durjan: <pre><p>Won't the goroutine for <code>addrCh</code> spaws as soon as <code>GetAddressChan</code> gets called and blocks until someone receives a value? At least in the second case in which I wrap <code>for</code> loop sending the values to <code>addrCh</code> in <code>GetAddressChan</code> in a goroutine instead of wrapping the <code>for</code> on <code>range</code> which receives values. It doesn't work in that case either. </p>
<p>All this seemed easier when I was reading the code/doing examples only. It seem very much like a simple example code, but I am unable to figure out what I am doing wrong.</p></pre>Sythe2o0: <pre><p>I'm not following what you're saying. Can you write up what you're trying in a snippet? This is an example of how this would work, without using the mail stuff: <a href="https://play.golang.org/p/7yvWLDMy4M" rel="nofollow">https://play.golang.org/p/7yvWLDMy4M</a></p></pre>vehlad_durjan: <pre><p>It's <code>bufio.Scan</code>'s doing I think. If I use a normal slice of strings instead of <code>bufio.Scan</code>, everything works as expected. I should study it more to find out what it is doing that is causing this, and how to work around it.</p></pre>bhiestand: <pre><p>Use a go routine in the function, and not in main. You don't need a buffered channel. You want the program to run until all addresses have been printed, but main will exit as soon as it starts the printing goroutine, as written.</p>
<p><a href="https://play.golang.org/p/Fu6qVdDesZ" rel="nofollow">https://play.golang.org/p/Fu6qVdDesZ</a></p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传