{"id":97426,"date":"2020-11-24T16:33:13","date_gmt":"2020-11-24T15:33:13","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=go&#038;p=97426"},"modified":"2021-05-06T07:30:11","modified_gmt":"2021-05-06T06:30:11","slug":"experimenting-with-go-type-parameters-generics-in-goland","status":"publish","type":"go","link":"https:\/\/blog.jetbrains.com\/en\/go\/2020\/11\/24\/experimenting-with-go-type-parameters-generics-in-goland","title":{"rendered":"Experimenting with Go Type Parameters (Generics) in GoLand"},"content":{"rendered":"<p>In today&#8217;s article, we will experiment with generics in Go, and their latest form, <em>Type Parameters<\/em>.<\/p>\n<p>Before we start, let&#8217;s take a quick look at the proposal&#8217;s history.<\/p>\n<h2 id=\"#history-of-generics-in-go\">History of generics in Go<\/h2>\n<p>Generics have been some of the most desired language features since Go&#8217;s inception. While the Go team never explicitly rejected them, they also did not have a good way to solve the problem of introducing generics to the language without making Go look and feel less Go-like.<\/p>\n<p>That was until August 2018, when the Go team <a href=\"https:\/\/go.googlesource.com\/proposal\/+\/master\/design\/go2draft-contracts.md\" target=\"_blank\" rel=\"noopener\">introduced the notion of Contracts<\/a> and started <a href=\"https:\/\/blog.golang.org\/why-generics\" target=\"_blank\" rel=\"noopener\">a big discussion with the community<\/a> about how generics could look and work in Go.<\/p>\n<p>Based on overwhelming feedback from the community, the <em>Contracts<\/em> proposal evolved and was modified until mid-2019. After a careful analysis of the proposal, it was determined that contracts are too similar to interfaces. So <em>Contracts<\/em> were dropped in favor of interfaces with a bit of twist, which we&#8217;ll return to later. Instead, <a href=\"https:\/\/blog.golang.org\/generics-next-step\" target=\"_blank\" rel=\"noopener\">a new proposal<\/a> was developed that simplified the learning curve and usage of <em>Type Parameters<\/em> in Go.<\/p>\n<p>Another year has passed since those last discussions of Type Parameters, and <a href=\"https:\/\/www.gophercon.com\/agenda\/session\/233094\" target=\"_blank\" rel=\"noopener\">this year&#8217;s GopherCon US<\/a> provided a better insight into how generic code could look and work in Go.<\/p>\n<p>Enough with the history for now. Let&#8217;s start writing some generic code for Go!<\/p>\n<h2 id=\"using-the-generics-go-sdk-in-goland\">Using the Generics Go SDK in GoLand<\/h2>\n<p>Before we can start coding, we&#8217;re going to need a special version of Go, and we&#8217;ll have to compile it ourselves.<\/p>\n<p>For the purposes of this article, I&#8217;m using GoLand 2020.3.<\/p>\n<p>Let&#8217;s use the <em>Get from VCS<\/em> action and select the Go repository <a href=\"https:\/\/go.googlesource.com\/go\" target=\"_blank\" rel=\"noopener\">https:\/\/go.googlesource.com\/go<\/a>. We clone that into a folder anywhere on our computer outside of the <em>GOPATH<\/em> and give the folder a name: <em>go-mainline<\/em>.<\/p>\n<p>Since we are compiling Go ourselves, we need to perform a few more steps than we would for a regular project.<\/p>\n<p>First, we&#8217;ll use the <em>Branches<\/em> feature, <em>Ctrl+Shift+`<\/em>, search for the <em>dev.go2go<\/em> branch, then we select the <em>Checkout<\/em> option.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-55173 size-full\" style=\"border: 1px solid #AAAAAA;\" alt=\"Clone Go with GoLand\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/1-Clone-Go-optimized.gif\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/1-Clone-Go-optimized.png\" width=\"750\" height=\"422\"><\/p>\n<p>Next, we&#8217;ll configure some environment variables and settings for the IDE:<\/p>\n<ul>\n<li>Under <a href=\"jetbrains:\/\/GoLand\/settings?name=Go--GOROOT\" target=\"_blank\">Settings\/Preferences | Go | GOROOT<\/a>, select the <code>&lt;No SDK&gt;<\/code> option.<\/li>\n<li>Under <a href=\"jetbrains:\/\/GoLand\/settings?name=Tools--Terminal\" target=\"_blank\">Settings\/Preferences | Tools | Terminal<\/a>, add the following environment variables:\n<ul>\n<li><code>GOROOT_BOOTSTRAP=&lt;path on your disk where a Go SDK newer than Go 1.14 is available&gt;<\/code>;<\/li>\n<li><code>CGO_ENABLED=0<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-55173 size-full\" style=\"border: 1px solid #AAAAAA;\" alt=\"Configure GoLand to compile the Go SDK\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/2-Configure-Go-SDK-compilation-environment-optimized.gif\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/2-Configure-Go-SDK-compilation-environment-optimized.png\" width=\"750\" height=\"422\"><\/p>\n<p><strong>Note:<\/strong> In this example, we are disabling <em>CGO<\/em> before building Go. which means that you will not be able to use <em>CGO<\/em> and test <em>Type Parameters<\/em>. If you need <em>CGO<\/em>, omit the <code>CGO_ENABLED=0<\/code> variable.<\/p>\n<p>With this step complete, we can open the built-in Terminal of the IDE with <em>Alt + F12<\/em> on <em>Windows\/Linux<\/em> or <em>Option + F12<\/em> on <em>macOS<\/em> and navigate to the <em>src<\/em> folder using <code>cd src<\/code>.<\/p>\n<p>Finally, let&#8217;s build Go itself by invoking the <em>make.bat<\/em> command on <em>Windows<\/em> or <em>make.sh<\/em> on <em>Linux\/macOS<\/em>.<\/p>\n<p>Once this process is done, we will have a Go version capable of interpreting the <em>Go 2 Type Parameters<\/em> proposal.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-55173 size-full\" style=\"border: 1px solid #AAAAAA;\" alt=\"Configure GoLand to compile the Go SDK\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/3-Compiling-the-Go-SDK-optimized.gif\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/3-Compiling-the-Go-SDK-optimized.png\" width=\"750\" height=\"422\"><\/p>\n<h2 id=\"writing-a-generics-based-hello-world-example\">Writing a generics-based Hello World example<\/h2>\n<p>Let&#8217;s create a <em>Go modules<\/em> project, using the <em>SDK<\/em> we just compiled as the project&#8217;s <em>Go SDK<\/em> and a new <em>main.go<\/em> file.<\/p>\n<p>For the demonstration code here, we&#8217;ll use the &#8220;best-in-class&#8221; sorting algorithm, <em>Bubble Sort<\/em>, to sort some books by their price.<\/p>\n<pre><code class=\"language-go\">package main\n\nimport \"fmt\"\n\ntype Book struct {\n    Name  string\n    Price int\n}\n\nfunc (x Book) Less(y Book) bool {\n    return y.Price &lt; x.Price\n}\n\ntype Lesser[T any] interface {\n    Less(y T) bool\n}\n\nfunc doSort[T Lesser[T]](a []T) {\n    for i := 0; i &lt; len(a)-1; i++ {\n        for j := i; j &lt; len(a); j++ {\n            if a[i].Less(a[j]) {\n                a[i], a[j] = a[j], a[i]\n            }\n        }\n    }\n}\n\nfunc main() {\n    a := []Book{\n        {\"Second Book\", 2},\n        {\"First Book\", 1},\n        {\"Fifth Book\", 5},\n        {\"Fourth Book\", 4},\n        {\"Sixth Book\", 6},\n        {\"Third Book\", 3},\n    }\n    doSort(a)\n    fmt.Println(a)\n}<\/code><\/pre>\n<p>You&#8217;ll immediately notice that the IDE prompts you to rename the file as a <code>.go2<\/code> file, so let&#8217;s do that.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-55173 size-full\" style=\"border: 1px solid #AAAAAA;\" alt=\"Configure GoLand to compile the Go SDK\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/4-New-Project-with-Type-Parameters-optimized.gif\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/4-New-Project-with-Type-Parameters-optimized.png\" width=\"750\" height=\"422\"><\/p>\n<h2 id=\"running-generics-code-in-goland\">Running generics code in GoLand<\/h2>\n<p>At the moment, GoLand does not support running generics code based on the <code>.go2<\/code> file format. So, we&#8217;ll have to configure an <em>External Tool<\/em> to make this work.<\/p>\n<p>Head over to <a href=\"jetbrains:\/\/GoLand\/settings?name=Tools--External+Tools\" target=\"_blank\">Settings\/Preferences | Tools | External Tools<\/a> and add a new tool with the name: <em>go2go<\/em>. For the description, we can use something like <em>Run the go2go experimental translation tool for Go Generics<\/em>. For the <em>Program<\/em>, use <em>$GOROOT$\/bin\/go.exe<\/em> if you are on <em>Windows<\/em> or <em>$GOROOT\/bin\/go<\/em> if you are on <em>Linux\/macOS<\/em>, and for <em>Arguments<\/em>, use <em>tool go2go build<\/em>. Finally, under the <em>Working directory<\/em>, use the <em>$FileDir$<\/em> macro and save the tool.<\/p>\n<p>Now let&#8217;s run the tool using <em>Search Everywhere<\/em>, <em>Shift + Shift<\/em>. This will compile our application and provide us with a binary file and another <code>.go<\/code> file. That file is not useful in this case and can be safely ignored.<\/p>\n<p>To run our application, invoke the <em>Terminal<\/em> using <em>Alt + F12<\/em> on <em>Windows\/Linux<\/em> or <em>Option + F12<\/em> on <em>macOS<\/em> and see the results!<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-55173 size-full\" style=\"border: 1px solid #AAAAAA;\" alt=\"Configure GoLand to compile the Go SDK\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/5-Run-Go-Type-Parameters-apps-from-GoLand-optimized.gif\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/5-Run-Go-Type-Parameters-apps-from-GoLand-optimized.png\" width=\"750\" height=\"422\"><\/p>\n<p><strong>Pro tip:<\/strong> Once you compile the binary for the first time, you will see the executable name created by the <em>go2go<\/em> tool, which allows you to create another <em>External Tool<\/em> that makes it easier to run.<\/p>\n<p><strong>Pro tip:<\/strong> You can assign shortcuts to <em>External Tools<\/em>, just like you would for standard IDE features. Search for the tool name in the IDE <a href=\"jetbrains:\/\/GoLand\/settings?name=Keymap\" target=\"_blank\">Settings\/Preferences | Keymap<\/a> and give it the shortcut you want.<\/p>\n<h2 id=\"share-your-code-with-the-world\">Share your code with the world<\/h2>\n<p>If you&#8217;d like to share your example with other Go programmers, you can do so directly from the IDE.<\/p>\n<p>Invoke the <em>Share in Playground<\/em> feature using <em>Ctrl + Alt + Shift + S<\/em> on <em>Windows\/Linux<\/em> or <em>CMD + Option + Shift + S<\/em> on <em>macOS<\/em>, and the IDE will share it automatically in <em>Playground<\/em>. Here&#8217;s my shared application: <a href=\"https:\/\/go2goplay.golang.org\/p\/kvXKTMX_Ye0\" target=\"_blank\" rel=\"noopener\">https:\/\/go2goplay.golang.org\/p\/kvXKTMX_Ye0<\/a>.<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-55173 size-full\" style=\"border: 1px solid #AAAAAA;\" alt=\"Configure GoLand to compile the Go SDK\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/6-Share-in-Playground-optimized.gif\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2020\/11\/6-Share-in-Playground-optimized.png\" width=\"750\" height=\"422\"><\/p>\n<p><strong>Note:<\/strong> Please keep in mind that all the code in the <em>Playground<\/em> is public, and anyone with the link can access it! Be careful with what you share there.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>And that&#8217;s it. We just wrote our first generics-enabled Go code.<br \/>\nThis feels pretty awesome, right?<\/p>\n<p>Despite still being in the proposal state, we can use <em>Type Parameters<\/em> to solve some problems that would otherwise be more complex. Fortunately, thanks to the compiler&#8217;s <em>Type Inference<\/em> feature, using <em>Type Parameters<\/em> does not feel strange, which was one of the Go team&#8217;s goals.<\/p>\n<p>However, there are also some important things to keep in mind.<\/p>\n<p>As of this article&#8217;s publication, the proposal to introduce generics in Go has not been accepted. Just as we saw a transition from <em>Contracts<\/em> to <em>Interfaces<\/em> for <em>Type Parameters<\/em>, the proposed solution may very well change again in the future, though this seems unlikely.<\/p>\n<p>So far, the editor&#8217;s support for generics is still in its early stages. We are working on improving it, but you may find that things don&#8217;t always work as expected. A few notable examples are the lack of inspections, quick-fixes, and refactorings. Completion also lags behind what you may be used to.<\/p>\n<p>Keep an eye on our blog, where we&#8217;ll publish news on the latest developments concerning this and other topics. If you want to get a peek at our latest changes, you can use the nightly builds during our EAP process. To learn more about how the EAP works, <a href=\"https:\/\/blog.jetbrains.com\/en\/go\/2020\/09\/25\/goland-2020-3-eap#what-is-eap\">click here<\/a>.<\/p>\n<p>If you have any questions or feedback about this or our product, let us know in the comments section below or on <a href=\"https:\/\/youtrack.jetbrains.com\" target=\"_blank\" rel=\"noopener\">our issue tracker<\/a>, or you can <a href=\"https:\/\/twitter.com\/GoLandIDE\" target=\"_blank\" rel=\"noopener\">tweet to us @GoLandIDE<\/a>.<\/p>\n","protected":false},"author":828,"featured_media":0,"comment_status":"closed","ping_status":"closed","template":"","categories":[4221,2347],"tags":[6245,2963,91],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/go\/97426"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/go"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/types\/go"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/users\/828"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/comments?post=97426"}],"version-history":[{"count":4,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/go\/97426\/revisions"}],"predecessor-version":[{"id":141264,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/go\/97426\/revisions\/141264"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media?parent=97426"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/categories?post=97426"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/tags?post=97426"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/cross-post-tag?post=97426"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}