14 Go's Declaration Syntax

Go‘s Declaration Syntax

7 July 2010

Introduction

Newcomers to Go wonder why the declaration syntax is different from the tradition established in the C family. In this post we‘ll compare the two approaches and explain why Go‘s declarations look as they do.

C syntax

First, let‘s talk about C syntax. C took an unusual and clever approach to declaration syntax. Instead of describing the types with special syntax, one writes an expression involving the item being declared, and states what type that expression will have. Thus

int x;

declares x to be an int: the expression ‘x‘ will have type int. In general, to figure out how to write the type of a new variable, write an expression involving that variable that evaluates to a basic type, then put the basic type on the left and the expression on the right.

Thus, the declarations

int *p;
int a[3];

state that p is a pointer to int because ‘*p‘ has type int, and that a is an array of ints because a[3] (ignoring the particular index value, which is punned to be the size of the array) has type int.

What about functions? Originally, C‘s function declarations wrote the types of the arguments outside the parens, like this:

int main(argc, argv)
    int argc;
    char *argv[];
{ /* ... */ }

Again, we see that main is a function because the expression main(argc, argv) returns an int. In modern notation we‘d write

int main(int argc, char *argv[]) { /* ... */ }

but the basic structure is the same.

This is a clever syntactic idea that works well for simple types but can get confusing fast. The famous example is declaring a function pointer. Follow the rules and you get this:

int (*fp)(int a, int b);

Here, fp is a pointer to a function because if you write the expression (*fp)(a, b) you‘ll call a function that returns int. What if one of fp‘s arguments is itself a function?

int (*fp)(int (*ff)(int x, int y), int b)

That‘s starting to get hard to read.

Of course, we can leave out the name of the parameters when we declare a function, so main can be declared

int main(int, char *[])

Recall that argv is declared like this,

char *argv[]

so you drop the name from the middle of its declaration to construct its type. It‘s not obvious, though, that you declare something of type char *[] by putting its name in the middle.

And look what happens to fp‘s declaration if you don‘t name the parameters:

int (*fp)(int (*)(int, int), int)

Not only is it not obvious where to put the name inside

int (*)(int, int)

it‘s not exactly clear that it‘s a function pointer declaration at all. And what if the return type is a function pointer?

int (*(*fp)(int (*)(int, int), int))(int, int)

It‘s hard even to see that this declaration is about fp.

You can construct more elaborate examples but these should illustrate some of the difficulties that C‘s declaration syntax can introduce.

There‘s one more point that needs to be made, though. Because type and declaration syntax are the same, it can be difficult to parse expressions with types in the middle. This is why, for instance, C casts always parenthesize the type, as in

(int)M_PI

Go syntax

Languages outside the C family usually use a distinct type syntax in declarations. Although it‘s a separate point, the name usually comes first, often followed by a colon. Thus our examples above become something like (in a fictional but illustrative language)

x: int
p: pointer to int
a: array[3] of int

These declarations are clear, if verbose - you just read them left to right. Go takes its cue from here, but in the interests of brevity it drops the colon and removes some of the keywords:

x int
p *int
a [3]int

There is no direct correspondence between the look of [3]int and how to use a in an expression. (We‘ll come back to pointers in the next section.) You gain clarity at the cost of a separate syntax.

Now consider functions. Let‘s transcribe the declaration for main as it would read in Go, although the real main function in Go takes no arguments:

func main(argc int, argv []string) int

Superficially that‘s not much different from C, other than the change from char arrays to strings, but it reads well from left to right:

function main takes an int and a slice of strings and returns an int.

Drop the parameter names and it‘s just as clear - they‘re always first so there‘s no confusion.

func main(int, []string) int

One merit of this left-to-right style is how well it works as the types become more complex. Here‘s a declaration of a function variable (analogous to a function pointer in C):

f func(func(int,int) int, int) int

Or if f returns a function:

f func(func(int,int) int, int) func(int, int) int

It still reads clearly, from left to right, and it‘s always obvious which name is being declared - the name comes first.

The distinction between type and expression syntax makes it easy to write and invoke closures in Go:

sum := func(a, b int) int { return a+b } (3, 4)

Pointers

Pointers are the exception that proves the rule. Notice that in arrays and slices, for instance, Go‘s type syntax puts the brackets on the left of the type but the expression syntax puts them on the right of the expression:

var a []int
x = a[1]

For familiarity, Go‘s pointers use the * notation from C, but we could not bring ourselves to make a similar reversal for pointer types. Thus pointers work like this

var p *int
x = *p

We couldn‘t say

var p *int
x = p*

because that postfix * would conflate with multiplication. We could have used the Pascal ^, for example:

var p ^int
x = p^

and perhaps we should have (and chosen another operator for xor), because the prefix asterisk on both types and expressions complicates things in a number of ways. For instance, although one can write

[]int("hi")

as a conversion, one must parenthesize the type if it starts with a *:

(*int)(nil)

Had we been willing to give up * as pointer syntax, those parentheses would be unnecessary.

So Go‘s pointer syntax is tied to the familiar C form, but those ties mean that we cannot break completely from using parentheses to disambiguate types and expressions in the grammar.

Overall, though, we believe Go‘s type syntax is easier to understand than C‘s, especially when things get complicated.

Notes

Go‘s declarations read left to right. It‘s been pointed out that C‘s read in a spiral! See The "Clockwise/Spiral Rule"by David Anderson.

By Rob Pike

Related articles

14 Go's Declaration Syntax

原文地址:https://www.cnblogs.com/kaid/p/9698467.html

时间: 2024-11-05 06:25:09

14 Go's Declaration Syntax的相关文章

Why Go's Declaration Syntax is better than C++?

[Why Go's Declaration Syntax is better than C++?] Newcomers to Go wonder why the declaration syntax is different from the tradition established in the C family. As descripbed in previous note the C++ use Clockwise/SpiralRule to parse the expression.

关于用turbo c 编译出现的 Declaration syntax error 错误 (未解决)

对着<深入体验c语言项目开发>中第一章编写俄罗斯方块这个游戏源代码将程序输进VC++,除去因为调用了turbo c 中的graphics.h ,而vc++中没有的这个库报错以外,没有报出其他的错 在turbo c 中却报错 /******************************初始化界面******************* ************************************************************/ void initialize(int

c++解释--百度百科

c++ C++是在C语言的基础上开发的一种面向对象编程语言,应用广泛:C++支持多种编程范式 --面向对象编程.泛型编程和过程化编程.最新正式标准C++于2014年8月18日公布.[1]  其编程领域众广,常用于系统开发,引擎开发等应用领域,是至今为止最受广大程序员受用的最强大编程语言之一,支持类:类.封装.重载等特性! 中文名 C++语言 外文名 The C++ Programming Language 类    别 计算机程序设计语言 创始人 Bjarne Stroustrup 创始公司 贝

[转]50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs

http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/ 50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs Go is a simple and fun language, but, like any other language, it has a few gotchas... Many of those gotc

c++基本特性

C++支持多种编程范式 --面向对象编程.泛型编程和过程化编程.常用于系统开发,引擎开发等应用领域. 与C的关系 C语言是C++的基础,C++和C语言在很多方面是兼容的. C语言是一个结构化语言,它的重点在于算法与数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境 条件)进行运算处理得到输出(或实现过程(事物)控制).C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通 过获取对象的状态信息得到输出或实现过程(事物)控制.所以C语言和C++的

使用CSharp编写Google Protobuf插件

什么是 Google Protocol Buffer? Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件.他们用于 RPC 系统和持续数据存储系统. Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化.它很适合做数据存储或 RPC 数据交换格式.可用于通讯协议.数据存储等

Programming with Objective-C 学习笔记

这几天把Programming with Objective-C再看了一边,发现有很多以前不明确的地方.现在把一些重要的点记下来,巩固学习效果. 1.Objective-C Classes Are also Objects(OC类也是对象) In Objective-C, a class is itself an object with an opaque type called Class. Classes can't have properties defined using the dec

MDK常见错误详解集合

错误代码及错误信息 错误释义 error 1: Out of memory 内存溢出 error 2: Identifier expected 缺标识符 error 3: Unknown identifier 未定义的标识符 error 4: Duplicate identifier 重复定义的标识符 error 5: Syntax error 语法错误 error 6: Error in real constant 实型常量错误 error 7: Error in integer consta

从Protocol Buffers 到 gRPC

从Protocol Buffers 到 gRPC 我们项目中准备使用Protocol Buffers来进行服务器和客户端的消息交互,采用gRPC开源框架,服务器使用Java,客户端有Android和iOS. 从Protocol Buffers 到 gRPC 一Protocol Buffers 文档 使用 1 定义一个消息类型 官方例子 2 字段限制 3 Tags 4 具体使用 Protoc源码的编译以及使用 1 安装ProtocolBuffer工具 2 使用protoc编译proto文件 二gR