p r a c t i c e i s a m e t h o d o r t e c h n i q u e t h a t h a s c o n s i s t e n t l y s h o w n r e s u l t s s u p e r i o r t o t h o s e a c h i e v e d w i t h o t h e r m e a n s " Techniques to write Go code that is simple, readable, maintainable.
e r s t r u c t { N a m e s t r i n g A g e Y e a r s i n t } f u n c ( g * G o p h e r ) W r i t e T o ( w i o . W r i t e r ) ( s i z e i n t 6 4 , e r r e r r o r ) { e r r = b i n a r y . W r i t e ( w , b i n a r y . L i t t l e E n d i a n , i n t 3 2 ( l e n ( g . N a m e ) ) ) i f e r r = = n i l { s i z e + = 4 v a r n i n t n , e r r = w . W r i t e ( [ ] b y t e ( g . N a m e ) ) s i z e + = i n t 6 4 ( n ) i f e r r = = n i l { e r r = b i n a r y . W r i t e ( w , b i n a r y . L i t t l e E n d i a n , i n t 6 4 ( g . A g e Y e a r s ) ) i f e r r = = n i l { s i z e + = 4 } r e t u r n } r e t u r n } r e t u r n }
( g * G o p h e r ) W r i t e T o ( w i o . W r i t e r ) ( s i z e i n t 6 4 , e r r e r r o r ) { e r r = b i n a r y . W r i t e ( w , b i n a r y . L i t t l e E n d i a n , i n t 3 2 ( l e n ( g . N a m e ) ) ) i f e r r ! = n i l { r e t u r n } s i z e + = 4 n , e r r : = w . W r i t e ( [ ] b y t e ( g . N a m e ) ) s i z e + = i n t 6 4 ( n ) i f e r r ! = n i l { r e t u r n } e r r = b i n a r y . W r i t e ( w , b i n a r y . L i t t l e E n d i a n , i n t 6 4 ( g . A g e Y e a r s ) ) i f e r r = = n i l { s i z e + = 4 } r e t u r n } Less nesting means less cognitive load on the reader
code t y p e b i n W r i t e r s t r u c t { w i o . W r i t e r s i z e i n t 6 4 e r r e r r o r } / / W r i t e w r i t e s a v a l u e t o t h e p r o v i d e d w r i t e r i n l i t t l e e n d i a n f o r m . f u n c ( w * b i n W r i t e r ) W r i t e ( v i n t e r f a c e { } ) { i f w . e r r ! = n i l { r e t u r n } i f w . e r r = b i n a r y . W r i t e ( w . w , b i n a r y . L i t t l e E n d i a n , v ) ; w . e r r = = n i l { w . s i z e + = i n t 6 4 ( b i n a r y . S i z e ( v ) ) } }
i t e r f u n c ( g * G o p h e r ) W r i t e T o ( w i o . W r i t e r ) ( i n t 6 4 , e r r o r ) { b w : = & b i n W r i t e r { w : w } b w . W r i t e ( i n t 3 2 ( l e n ( g . N a m e ) ) ) b w . W r i t e ( [ ] b y t e ( g . N a m e ) ) b w . W r i t e ( i n t 6 4 ( g . A g e Y e a r s ) ) r e t u r n b w . s i z e , b w . e r r }
( w * b i n W r i t e r ) W r i t e ( v i n t e r f a c e { } ) { i f w . e r r ! = n i l { r e t u r n } s w i t c h v . ( t y p e ) { c a s e s t r i n g : s : = v . ( s t r i n g ) w . W r i t e ( i n t 3 2 ( l e n ( s ) ) ) w . W r i t e ( [ ] b y t e ( s ) ) c a s e i n t : i : = v . ( i n t ) w . W r i t e ( i n t 6 4 ( i ) ) d e f a u l t : i f w . e r r = b i n a r y . W r i t e ( w . w , b i n a r y . L i t t l e E n d i a n , v ) ; w . e r r = = n i l { w . s i z e + = i n t 6 4 ( b i n a r y . S i z e ( v ) ) } } } f u n c ( g * G o p h e r ) W r i t e T o ( w i o . W r i t e r ) ( i n t 6 4 , e r r o r ) { b w : = & b i n W r i t e r { w : w } b w . W r i t e ( g . N a m e ) b w . W r i t e ( g . A g e Y e a r s ) r e t u r n b w . s i z e , b w . e r r }
( w * b i n W r i t e r ) W r i t e ( v i n t e r f a c e { } ) { i f w . e r r ! = n i l { r e t u r n } s w i t c h x : = v . ( t y p e ) { c a s e s t r i n g : w . W r i t e ( i n t 3 2 ( l e n ( x ) ) ) w . W r i t e ( [ ] b y t e ( x ) ) c a s e i n t : w . W r i t e ( i n t 6 4 ( x ) ) d e f a u l t : i f w . e r r = b i n a r y . W r i t e ( w . w , b i n a r y . L i t t l e E n d i a n , v ) ; w . e r r = = n i l { w . s i z e + = i n t 6 4 ( b i n a r y . S i z e ( v ) ) } } }
n W r i t e r s t r u c t { w i o . W r i t e r b u f b y t e s . B u f f e r e r r e r r o r } / / W r i t e w r i t e s a v a l u e t o t h e p r o v i d e d w r i t e r i n l i t t l e e n d i a n f o r m . f u n c ( w * b i n W r i t e r ) W r i t e ( v i n t e r f a c e { } ) { i f w . e r r ! = n i l { r e t u r n } s w i t c h x : = v . ( t y p e ) { c a s e s t r i n g : w . W r i t e ( i n t 3 2 ( l e n ( x ) ) ) w . W r i t e ( [ ] b y t e ( x ) ) c a s e i n t : w . W r i t e ( i n t 6 4 ( x ) ) d e f a u l t : w . e r r = b i n a r y . W r i t e ( & w . b u f , b i n a r y . L i t t l e E n d i a n , v ) } }
h w r i t e s a n y p e n d i n g v a l u e s i n t o t h e w r i t e r i f n o e r r o r h a s o c c u r r e d . / / I f a n e r r o r h a s o c c u r r e d , e a r l i e r o r w i t h a w r i t e b y F l u s h , t h e e r r o r i s / / r e t u r n e d . f u n c ( w * b i n W r i t e r ) F l u s h ( ) ( i n t 6 4 , e r r o r ) { i f w . e r r ! = n i l { r e t u r n 0 , w . e r r } r e t u r n w . b u f . W r i t e T o ( w . w ) } f u n c ( g * G o p h e r ) W r i t e T o ( w i o . W r i t e r ) ( i n t 6 4 , e r r o r ) { b w : = & b i n W r i t e r { w : w } b w . W r i t e ( g . N a m e ) b w . W r i t e ( g . A g e Y e a r s ) r e t u r n b w . F l u s h ( ) }
( ) { h t t p . H a n d l e F u n c ( " / " , h a n d l e r ) } f u n c h a n d l e r ( w h t t p . R e s p o n s e W r i t e r , r * h t t p . R e q u e s t ) { e r r : = d o T h i s ( ) i f e r r ! = n i l { h t t p . E r r o r ( w , e r r . E r r o r ( ) , h t t p . S t a t u s I n t e r n a l S e r v e r E r r o r ) l o g . P r i n t f ( " h a n d l i n g % q : % v " , r . R e q u e s t U R I , e r r ) r e t u r n } e r r = d o T h a t ( ) i f e r r ! = n i l { h t t p . E r r o r ( w , e r r . E r r o r ( ) , h t t p . S t a t u s I n t e r n a l S e r v e r E r r o r ) l o g . P r i n t f ( " h a n d l i n g % q : % v " , r . R e q u e s t U R I , e r r ) r e t u r n } }
( ) { h t t p . H a n d l e F u n c ( " / " , e r r o r H a n d l e r ( b e t t e r H a n d l e r ) ) } f u n c e r r o r H a n d l e r ( f f u n c ( h t t p . R e s p o n s e W r i t e r , * h t t p . R e q u e s t ) e r r o r ) h t t p . H a n d l e r F u n c { r e t u r n f u n c ( w h t t p . R e s p o n s e W r i t e r , r * h t t p . R e q u e s t ) { e r r : = f ( w , r ) i f e r r ! = n i l { h t t p . E r r o r ( w , e r r . E r r o r ( ) , h t t p . S t a t u s I n t e r n a l S e r v e r E r r o r ) l o g . P r i n t f ( " h a n d l i n g % q : % v " , r . R e q u e s t U R I , e r r ) } } } f u n c b e t t e r H a n d l e r ( w h t t p . R e s p o n s e W r i t e r , r * h t t p . R e q u e s t ) e r r o r { i f e r r : = d o T h i s ( ) ; e r r ! = n i l { r e t u r n f m t . E r r o r f ( " d o i n g t h i s : % v " , e r r ) } i f e r r : = d o T h a t ( ) ; e r r ! = n i l { r e t u r n f m t . E r r o r f ( " d o i n g t h a t : % v " , e r r ) } r e t u r n n i l }
Import statements, related groups separated by blank lines. i m p o r t ( " f m t " " i o " " l o g " " c o d e . g o o g l e . c o m / p / g o . n e t / w e b s o c k e t " ) The rest of the code starting with the most significant types, and ending with helper function and types.
/ / P a c k a g e p l a y g r o u n d r e g i s t e r s a n H T T P h a n d l e r a t " / c o m p i l e " t h a t / / p r o x i e s r e q u e s t s t o t h e g o l a n g . o r g p l a y g r o u n d s e r v i c e . p a c k a g e p l a y g r o u n d Exported identifiers appear in g o d o c , they should be documented correctly. / / A u t h o r r e p r e s e n t s t h e p e r s o n w h o w r o t e a n d / o r i s p r e s e n t i n g t h e d o c u m e n t . t y p e A u t h o r s t r u c t { E l e m [ ] E l e m } / / T e x t E l e m r e t u r n s t h e f i r s t t e x t e l e m e n t s o f t h e a u t h o r d e t a i l s . / / T h i s i s u s e d t o d i s p l a y t h e a u t h o r ' n a m e , j o b t i t l e , a n d c o m p a n y / / w i t h o u t t h e c o n t a c t d e t a i l s . f u n c ( p * A u t h o r ) T e x t E l e m ( ) ( e l e m s [ ] E l e m ) { Generated documentation (http://godoc.org/code.google.com/p/go.talks/pkg/present#Author) Gocode: documenting Go code (http://blog.golang.org/godoc-documenting-go-code)
better. Try to find the shortest name that is self explanatory. Prefer M a r s h a l I n d e n t to M a r s h a l W i t h I n d e n t a t i o n . Don't forget that the package name will appear before the identifier you chose. In package e n c o d i n g / j s o n we find the type E n c o d e r , not J S O N E n c o d e r . It is referred as j s o n . E n c o d e r .
multiple files? Avoid very long files The n e t / h t t p package from the standard library contains 15734 lines in 47 files. Separate code and tests n e t / h t t p / c o o k i e . g o and n e t / h t t p / c o o k i e _ t e s t . g o are both part of the h t t p package. Test code is compiled only at test time. Separated package documentation When we have more than one file in a package, it's convention to create a d o c . g o containing the package documentation.
some others are not. A package defining some network protocol might be reused while one defining an executable command may not. github.com/bradfitz/camlistore (https://github.com/bradfitz/camlistore)
from before t y p e G o p h e r s t r u c t { N a m e s t r i n g A g e Y e a r s i n t } We could define this method f u n c ( g * G o p h e r ) W r i t e T o F i l e ( f * o s . F i l e ) ( i n t 6 4 , e r r o r ) { But using a concrete type makes this code difficult to test, so we use an interface. f u n c ( g * G o p h e r ) W r i t e T o R e a d W r i t e r ( r w i o . R e a d W r i t e r ) ( i n t 6 4 , e r r o r ) { And, since we're using an interface, we should ask only for the methods we need. f u n c ( g * G o p h e r ) W r i t e T o W r i t e r ( f i o . W r i t e r ) ( i n t 6 4 , e r r o r ) {
( " c o d e . g o o g l e . c o m / p / g o . t a l k s / 2 0 1 3 / b e s t p r a c t i c e s / f u n c d r a w / d r a w e r " " c o d e . g o o g l e . c o m / p / g o . t a l k s / 2 0 1 3 / b e s t p r a c t i c e s / f u n c d r a w / p a r s e r " ) / / P a r s e t h e t e x t i n t o a n e x e c u t a b l e f u n c t i o n . f , e r r : = p a r s e r . P a r s e ( t e x t ) i f e r r ! = n i l { l o g . F a t a l f ( " p a r s e % q : % v " , t e x t , e r r ) } / / C r e a t e a n i m a g e p l o t t i n g t h e f u n c t i o n . m : = d r a w e r . D r a w ( f , * w i d t h , * h e i g h t , * x m i n , * x m a x ) / / E n c o d e t h e i m a g e i n t o t h e s t a n d a r d o u t p u t . e r r = p n g . E n c o d e ( o s . S t d o u t , m ) i f e r r ! = n i l { l o g . F a t a l f ( " e n c o d e i m a g e : % v " , e r r ) }
d F u n c s t r u c t { t e x t s t r i n g e v a l f u n c ( f l o a t 6 4 ) f l o a t 6 4 } f u n c P a r s e ( t e x t s t r i n g ) ( * P a r s e d F u n c , e r r o r ) { f , e r r : = p a r s e ( t e x t ) i f e r r ! = n i l { r e t u r n n i l , e r r } r e t u r n & P a r s e d F u n c { t e x t : t e x t , e v a l : f } , n i l } f u n c ( f * P a r s e d F u n c ) E v a l ( x f l o a t 6 4 ) f l o a t 6 4 { r e t u r n f . e v a l ( x ) } f u n c ( f * P a r s e d F u n c ) S t r i n g ( ) s t r i n g { r e t u r n f . t e x t }
m a g e " " c o d e . g o o g l e . c o m / p / g o . t a l k s / 2 0 1 3 / b e s t p r a c t i c e s / f u n c d r a w / p a r s e r " ) / / D r a w d r a w s a n i m a g e s h o w i n g a r e n d e r i n g o f t h e p a s s e d P a r s e d F u n c . f u n c D r a w P a r s e d F u n c ( f p a r s e r . P a r s e d F u n c ) i m a g e . I m a g e { Avoid dependency by using an interface. i m p o r t " i m a g e " / / F u n c t i o n r e p r e s e n t a d r a w a b l e m a t h e m a t i c a l f u n c t i o n . t y p e F u n c t i o n i n t e r f a c e { E v a l ( f l o a t 6 4 ) f l o a t 6 4 } / / D r a w d r a w s a n i m a g e s h o w i n g a r e n d e r i n g o f t h e p a s s e d F u n c t i o n . f u n c D r a w ( f F u n c t i o n ) i m a g e . I m a g e {
testing easier. p a c k a g e d r a w e r i m p o r t ( " m a t h " " t e s t i n g " ) t y p e T e s t F u n c f u n c ( f l o a t 6 4 ) f l o a t 6 4 f u n c ( f T e s t F u n c ) E v a l ( x f l o a t 6 4 ) f l o a t 6 4 { r e t u r n f ( x ) } v a r ( i d e n t = T e s t F u n c ( f u n c ( x f l o a t 6 4 ) f l o a t 6 4 { r e t u r n x } ) s i n = T e s t F u n c ( m a t h . S i n ) ) f u n c T e s t D r a w _ I d e n t ( t * t e s t i n g . T ) { m : = D r a w ( i d e n t ) / / V e r i f y o b t a i n e d i m a g e .
use it sequentially? f u n c d o C o n c u r r e n t l y ( j o b s t r i n g , e r r c h a n e r r o r ) { g o f u n c ( ) { f m t . P r i n t l n ( " d o i n g j o b " , j o b ) t i m e . S l e e p ( 1 * t i m e . S e c o n d ) e r r < - e r r o r s . N e w ( " s o m e t h i n g w e n t w r o n g ! " ) } ( ) } f u n c m a i n ( ) { j o b s : = [ ] s t r i n g { " o n e " , " t w o " , " t h r e e " } e r r c : = m a k e ( c h a n e r r o r ) f o r _ , j o b : = r a n g e j o b s { d o C o n c u r r e n t l y ( j o b , e r r c ) } f o r _ = r a n g e j o b s { i f e r r : = < - e r r c ; e r r ! = n i l { f m t . P r i n t l n ( e r r ) } } } Run
concurrently is easy. f u n c d o ( j o b s t r i n g ) e r r o r { f m t . P r i n t l n ( " d o i n g j o b " , j o b ) t i m e . S l e e p ( 1 * t i m e . S e c o n d ) r e t u r n e r r o r s . N e w ( " s o m e t h i n g w e n t w r o n g ! " ) } f u n c m a i n ( ) { j o b s : = [ ] s t r i n g { " o n e " , " t w o " , " t h r e e " } e r r c : = m a k e ( c h a n e r r o r ) f o r _ , j o b : = r a n g e j o b s { g o f u n c ( j o b s t r i n g ) { e r r c < - d o ( j o b ) } ( j o b ) } f o r _ = r a n g e j o b s { i f e r r : = < - e r r c ; e r r ! = n i l { f m t . P r i n t l n ( e r r ) } } } Run
struct with a chan to communicate with a goroutine t y p e S e r v e r s t r u c t { q u i t c h a n b o o l } f u n c N e w S e r v e r ( ) * S e r v e r { s : = & S e r v e r { m a k e ( c h a n b o o l ) } g o s . r u n ( ) r e t u r n s } f u n c ( s * S e r v e r ) r u n ( ) { f o r { s e l e c t { c a s e < - s . q u i t : f m t . P r i n t l n ( " f i n i s h i n g t a s k " ) t i m e . S l e e p ( t i m e . S e c o n d ) f m t . P r i n t l n ( " t a s k d o n e " ) s . q u i t < - t r u e r e t u r n c a s e < - t i m e . A f t e r ( t i m e . S e c o n d ) : f m t . P r i n t l n ( " r u n n i n g t a s k " ) } } }
( s * S e r v e r ) S t o p ( ) { f m t . P r i n t l n ( " s e r v e r s t o p p i n g " ) s . q u i t < - t r u e < - s . q u i t f m t . P r i n t l n ( " s e r v e r s t o p p e d " ) } f u n c m a i n ( ) { s : = N e w S e r v e r ( ) t i m e . S l e e p ( 2 * t i m e . S e c o n d ) s . S t o p ( ) } Run
s e n d M s g ( m s g , a d d r s t r i n g ) e r r o r { c o n n , e r r : = n e t . D i a l ( " t c p " , a d d r ) i f e r r ! = n i l { r e t u r n e r r } d e f e r c o n n . C l o s e ( ) _ , e r r = f m t . F p r i n t ( c o n n , m s g ) r e t u r n e r r } f u n c m a i n ( ) { a d d r : = [ ] s t r i n g { " l o c a l h o s t : 8 0 8 0 " , " h t t p : / / g o o g l e . c o m " } e r r : = b r o a d c a s t M s g ( " h i " , a d d r ) t i m e . S l e e p ( t i m e . S e c o n d ) i f e r r ! = n i l { f m t . P r i n t l n ( e r r ) r e t u r n } f m t . P r i n t l n ( " e v e r y t h i n g w e n t f i n e " ) }
blocked on the chan write the goroutine holds a reference to the chan the chan will never be garbage collected f u n c b r o a d c a s t M s g ( m s g s t r i n g , a d d r s [ ] s t r i n g ) e r r o r { e r r c : = m a k e ( c h a n e r r o r ) f o r _ , a d d r : = r a n g e a d d r s { g o f u n c ( a d d r s t r i n g ) { e r r c < - s e n d M s g ( m s g , a d d r ) f m t . P r i n t l n ( " d o n e " ) } ( a d d r ) } f o r _ = r a n g e a d d r s { i f e r r : = < - e r r c ; e r r ! = n i l { r e t u r n e r r } } r e t u r n n i l } Run
can't predict the capacity of the channel? f u n c b r o a d c a s t M s g ( m s g s t r i n g , a d d r s [ ] s t r i n g ) e r r o r { e r r c : = m a k e ( c h a n e r r o r , l e n ( a d d r s ) ) f o r _ , a d d r : = r a n g e a d d r s { g o f u n c ( a d d r s t r i n g ) { e r r c < - s e n d M s g ( m s g , a d d r ) f m t . P r i n t l n ( " d o n e " ) } ( a d d r ) } f o r _ = r a n g e a d d r s { i f e r r : = < - e r r c ; e r r ! = n i l { r e t u r n e r r } } r e t u r n n i l } Run
b r o a d c a s t M s g ( m s g s t r i n g , a d d r s [ ] s t r i n g ) e r r o r { e r r c : = m a k e ( c h a n e r r o r ) q u i t : = m a k e ( c h a n s t r u c t { } ) d e f e r c l o s e ( q u i t ) f o r _ , a d d r : = r a n g e a d d r s { g o f u n c ( a d d r s t r i n g ) { s e l e c t { c a s e e r r c < - s e n d M s g ( m s g , a d d r ) : f m t . P r i n t l n ( " d o n e " ) c a s e < - q u i t : f m t . P r i n t l n ( " q u i t " ) } } ( a d d r ) } f o r _ = r a n g e a d d r s { i f e r r : = < - e r r c ; e r r ! = n i l { r e t u r n e r r } } r e t u r n n i l } Run
2. Avoid repetition when possible 3. Important code goes first 4. Document your code 5. Shorter is better 6. Packages with multiple files 7. Make your packages "go get"-able 8. Ask for what you need 9. Keep independent packages independent 10. Avoid concurrency in your API 11. Use goroutines to manage state 12. Avoid goroutine leaks
tour.golang.org (http://tour.golang.org) Other talks Lexical scanning with Go video (http://www.youtube.com/watch?v=HxaD_trXwRE) Concurrency is not parallelism video (http://vimeo.com/49718712) Go concurrency patterns video (http://www.youtube.com/watch?v=f6kdp27TYZs) Advanced Go concurrency patterns video (http://www.youtube.com/watch?v=QDDwwePbDtw)