Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Winkle-Daddy
Mar 10, 2007
I just started learning Go this last week and like it quite a bit. I'm hoping it can fill the wasteland void that we currently use Ruby for in a lot of places. There's a whole lot of conversations I have that go something like "Oh yeah, I wrote a script for that, just git clone the repo it's in and use it......well yeah, you have to do bundle install first to get the required gems...no bundler is a thing you have to install....got it? Great. Hmmm, it's still not working? What version of Ruby are you running? Ah, just point RVM at a new version....oh, you installed a system Ruby? gently caress. Well, try this Python thing instead, yeah, that won't work on RHEL 5.6 because Python is too old."

I think any hit my productivity takes to learn my way around go will be made up for in that I won't be troubleshooting installs of Ruby/Python on other developer systems all goddamn day. Whelp, that's my story! I just wanted to say that as someone who is coming from a Python/C background, Go feels super duper natural and I like it a lot!

Adbot
ADBOT LOVES YOU

Winkle-Daddy
Mar 10, 2007
I'm reading some about how interfaces work here: http://www.golangbootcamp.com/book/interfaces and had a question about this.

To save everyone from clicking on the link, it provides some example code like this:
code:
package main

import (
	"fmt"
)

type User struct {
	FirstName, LastName string
}

func (u *User) Name() string {
	return fmt.Sprintf("%s %s", u.FirstName, u.LastName)
}

type Namer interface {
	Name() string
}

func Greet(n Namer) string {
	return fmt.Sprintf("Dear %s", n.Name())
}

func main() {
	u := &User{"Matt", "Aimonetti"}
	fmt.Println(Greet(u))
}
My question is, can the Name() function take parameters through an interface? What would be the suggested method for passing arguments be? The only way I could think to do it in this example would be to extend the User struct to include any options I want set, then set them when I dereference into the variable "u" which (should) make it accessible to the function the interface calls. That feels kind of dirty though, would that be considered the "go way" of doing things?

e: sorry if this is a dumb question, it's just that everyone says "Start doing things the go way to learn better!" and I'm not exactly sure what the go way is in this case.

Winkle-Daddy fucked around with this message at 22:18 on Aug 28, 2015

Winkle-Daddy
Mar 10, 2007
Hmm, okay, but here:

code:
func main() {
	u := &User{"Matt", "Aimonetti"}
	fmt.Println(Greet(u))
}
What's the syntax for getting my argument into the function since u was passed into Greet? This part is my specific question, which I probably could have asked better.

Winkle-Daddy
Mar 10, 2007
Oh duh. Okay. Thank you! :)

Winkle-Daddy
Mar 10, 2007
I've learned a lot more about Go and like it quite a bit. But I ran into a weird thing that I'm having a hard time googling the 'correct' way to do it. Hopefully someone can help out. I have a struct that I want to use for reading/writing config files. My struct is like this:
code:
type Configuration struct {
	ValueOne   string
	ValueArray    []string
	ValueThree    string
	IntValue int
}
This should allow me to unmarshall the following json into this struct:
code:
{
    "ValueOne": "foo"
    "ValueArray": ["foo", "bar"]
    "ValueThree": "foobar"
    "IntValue": 5
}
But what I'm having a hard time figuring out is how I marshall the data if I want to just write an empty config file on first run. For example:
code:
dataToWrite := Configuration{"foo",
			["foo", "bar"], // ideally this would just be [], or some other empty value intended to be replaced
			"foobar",
			5}
d, _ := json.MarshalIndent(dataToWrite, "", "    ")
err := ioutil.WriteFile(configFile, d, 0600)
I did find this excellent blog post. With some re-working I could go this route, but it seems silly that if I have a defined struct I don't use it to marshall and unmarshall my data as opposed to something like this (which should work):
code:
dataToWrite := []byte(`{"ValueOne":"foo","ValueArray":["foo","bar"],"ValueThree":"foobar","IntValue":5}`)
Do I just settle for what I think is the less clean looking option or is there a way to fix what I am doing?

Winkle-Daddy
Mar 10, 2007

Lonely Wolf posted:

You're just using the wrong syntax for the composite literal, if I'm understanding the question:

https://play.golang.org/p/uebkyYxuCN

Oh! That makes perfect sense looking at it now. Heh. Thanks! This is what happens when you're coming from Python, I guess...

COOL CORN posted:

Does anyone use Go for game development? I'm curious what the most popular use cases of Go are at the moment - I guess mostly web-based?

We have found that go is best used as part of your service oriented architecture stack. It's really easy to get individual go binaries deployed and controlled through systemd that do everything from host simple web services to doing more advanced manipulation. It's not very good for game dev, and UI libs aren't well supported like at all due to how big your binary becomes due to the static linking involved. Someone who has actually tried could probably elaborate more, but I see it as 90% replacing your bash magic glue in your tech stack.

Winkle-Daddy fucked around with this message at 17:39 on Apr 7, 2016

Winkle-Daddy
Mar 10, 2007
As someone coming from vim (albeit with a bunch of plug-ins) I must say that Atom is great. I've always thought that IDEA "feels" really sluggish (whatever that means, I feel the same about pyCharm, intellij, aptana, eclipse, etc), while Atom feels very snappy and unobtrusive. The only issue I've run into so far is that one of the plug-ins appears to eat my imports if I'm importing two different directories in the same Github repo. That's the sole issue I've run into so far, though.

I also find that since my Go projects are not monolithic but tend to be more service oriented, a full IDE experience is probably not as helpful as it would be if I was working on a complex Python project or something.

Winkle-Daddy
Mar 10, 2007
Uhhhhh, stupid question about how channels work and my Google-fu is failing me this afternoon.

Let's say I'm creating a hypothetical data struct that's a bunch of hostnames/platforms. Now let's say I want to ping each of these once and just return a confirmation that a certain host was completed back to the main function.

My first inclination is to do something like this:
code:
package main

import (
	"fmt"
)

type hostInfo struct {
	hostname string
	platform string
}

func pinger(data hostInfo, done chan hostInfo) {
	// Code to do some ping stuff...
	done <- data
}

func main() {
	pchan := make(chan hostInfo)
	someData := []hostInfo{}
	someData = append(someData, thingie{"10.1.1.1", "linux"})
	someData = append(someData, thingie{"10.1.1.2", "linux"})
	someData = append(someData, thingie{"10.1.1.3", "linux"})
	someData = append(someData, thingie{"10.1.1.4", "linux"})
	for _, v := range someData {
		go pinger(v, pchan)
	}
	close(pchan)
	for data := range pchan {
		fmt.Printlf("Finished with %s of type %s.", data.hostname, data.platform)
	}
}
This code just exits immediately and I assume it's because I have nothing receiving on "pchan", but I thought the range built in would receive on a channel until it's closed, but it doesn't appear to be working quite like I'd expect.

This is just a stupid simple example to help me understand how these things work. In a real example I am quite certain my design would be significantly different.

e: okay, the problem is slightly different than I thought. If I close the channel, then I can't receive on it anymore. If I remove the close, my output looks like:
code:
./go-concurrency
Finished with 10.1.1.1 of type linux.
Finished with 10.1.1.2 of type linux.
Finished with 10.1.1.3 of type linux.
Finished with 10.1.1.4 of type linux.
fatal error: all goroutines are asleep - deadlock!
So I must just be messing a way to essentially do a deferred close (but not really because if I defer it, it's a deadlock as it's in main).

Winkle-Daddy fucked around with this message at 22:35 on Jun 27, 2016

Winkle-Daddy
Mar 10, 2007

so loving future posted:

You're closing your channel before you read it.

You probably want to do something like this:

code:
func main() {
.......
	for _, v := range someData {
		go pinger(v, pchan)
	}
	defer close(pchan)

	for i := 0; i < len(someData); i++ {
		select {
		case data := <-pchan:
			fmt.Printf("Finished with %s of type %s.\n", data.hostname, data.platform)
		}
	}
}

That was odd, I did:
code:
for _ := range pchan
instead of
code:
for _ := range someData
Which then works as expected. As does your for loop. I was trying to iterate over the channel instead of the values in my slice. Whoops.


Dangerllama posted:

If you're unsure about the number of things you're going to pass on the channel, you can also use a WaitGroup. Notice that the WaitGroup needs to live in its own goroutine in order to be able to close the channel:

code:
package main

import (
	"fmt"
	"sync"
)

type hostInfo struct {
	hostname string
	platform string
}

func pinger(data hostInfo, done chan hostInfo, wg *sync.WaitGroup) {
	defer wg.Done()
	// Code to do some ping stuff...
	done <- data
}

func main() {
	pchan := make(chan hostInfo)
	var wg sync.WaitGroup

	someData := []hostInfo{}
	someData = append(someData, hostInfo{"10.1.1.1", "linux"})
	someData = append(someData, hostInfo{"10.1.1.2", "linux"})
	someData = append(someData, hostInfo{"10.1.1.3", "linux"})
	someData = append(someData, hostInfo{"10.1.1.4", "linux"})

	for _, v := range someData {
		wg.Add(1)
		go pinger(v, pchan, &wg)
	}

	go func() {
		wg.Wait()
		close(pchan)
	}()

	for data := range pchan {
		fmt.Printf("Finished with %s of type %s.\n", data.hostname, data.platform)
	}

}

Thanks! I'll see if this is easier to work with next. I have a project where I'll need to spin off a large work load into various go routines and then organize them, so I'm just creating super small examples to help get my brain around how this all works.

Winkle-Daddy
Mar 10, 2007

Thanks! These are really helpful and I should stop trying to learn from the language spec and standard library docs alone. :downs:

Lonely Wolf posted:

Missed the party, but you can just write

code:
for range someData {
   //loops len(someData) times
}
if you don't care about the index or value, as of go 1.4

Thanks for the tip! I thought it was silly you needed to use as bit bucket there if you didn't care about the iterator at all, now I know.

Winkle-Daddy
Mar 10, 2007
Help!

I'm fixing up some co-workers code. He needed to add some cross platform support to windows. It's laid out like this now:

code:
main.go
networking.go
utils.go
utils_windows.go
Most of utils_windows.go is the same as utils.go with a few minor edits. This seems silly to me, in my head I should be able to do something like:
code:
utils_common.go
utils_windows.go
utils_linux.go
In this way I would like everything OS specific in the file indicated for that OS. The problem is that if I have:

code:
utils_windows.go

func foo() bar {
  //do stuff
}

-------------------------
utils_linux.go

func foo() bar {
  // do stuff
}

-------------------------
utils_common.go

func foobar() blah {
  blah := foo()
}
In this sort of a scenario, utils_common can't find the OS specific function. What's the best way to unfuck this code in such a way that my linters won't freak out about the fact that every OS specific function he added is defined twice?

e: this is highly simplified, the fuckening is great with this one :\ It's actually dozens and dozens of dupe functions

Winkle-Daddy
Mar 10, 2007
e: ^^^ yeah, I removed _common.go to get my makefile to build, which now looks something like this:

code:
LINUXBINARY=foo
OSXBINARY=foo-osx
WINDOWSBINARY=foo.exe

VERSION=1.2.0
BUILDTIME=`date +%FT%T%z`

WIN_FILES = networking_windows.go utils_windows.go
LINUX_FILES = utils_linux.go
COMMON_FILES = client.go types.go main.go utils.go networking.go

# Things are passed in with LDFLAGS
LDFLAGS=-ldflags "-X main.version=${VERSION} -X main.buildTime=${BUILDTIME}"

all: linux windows osx

linux: main.go
	env GOOS=linux GOARCH=amd64 go build ${LDFLAGS} -o ${LINUXBINARY} -v $(LINUX_FILES) $(COMMON_FILES)

windows: main.go
	env GOOS=windows GOARCH=amd64 go build ${LDFLAGS} -o ${WINDOWSBINARY} -v $(WIN_FILES) $(COMMON_FILES)


osx: main.go
	env GOOS=darwin GOARCH=amd64 go build ${LDFLAGS} -o ${OSXBINARY} -v $(LINUX_FILES) $(COMMON_FILES)

.PHONY: clean
clean:
	if [ -f ${WINDOWSBINARY} ] ; then rm ${WINDOWSBINARY} ; fi
	if [ -f ${LINUXBINARY} ] ; then rm ${LINUXBINARY} ; fi
	if [ -f ${OSXBINARY} ] ; then rm ${OSXBINARY} ; fi
So it builds...and I'm probably going overkill using both OS suffixes and defining different build files as my common files...

Hmm, I think this may just be a linter thing, where it complains that functions in an OS specific file are undefined...because it does seem to build okay so long as no functions are re-declared between the common file and the OS specific file.

Winkle-Daddy fucked around with this message at 19:37 on Nov 16, 2016

Adbot
ADBOT LOVES YOU

Winkle-Daddy
Mar 10, 2007
So...doing things this way seems to have broken my ability to run automated tests. If I build, and provide as args every file I want to include it works fine. If I try to use build tags to control how things build I get the following error:
code:
go build .
./networking.go:132: undefined: createFSFromTemplate
./networking.go:136: undefined: modFSFromTemplate
./networking.go:140: undefined: cleanMockFS
./utils.go:28: undefined: convertTextToJSON
The functions that are being complained about exist only in the _[os] version of the file. Is there some other magic I can do to make this work or are all my options at this point potential time sinks?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply