首页 > 其他分享 >make是不是go的关键字

make是不是go的关键字

时间:2023-11-09 20:45:07浏览次数:29  
标签:loc type make 关键字 keywords arg go Expression

keyword

go语言介绍中标榜的一个重要特点是语法简单,这里有一个不同语言关键字的个数,同样是为了防止网页打不开或者丢失,这里单独复制一份:

  • C (ANSI (C89)) (32 keywords)
  • C (C11) (44 keywords)
  • C (C17) (44 keywords)
  • C (C99) (37 keywords)
  • C# (8.0) (107 keywords)
  • C++ (C++03) (74 keywords)
  • C++ (C++11) (84 keywords)
  • C++ (C++14) (84 keywords)
  • C++ (C++17) (84 keywords)
  • C++ (C++20) (92 keywords)
  • C++ (C++98) (74 keywords)
  • Dart (2.2) (33 keywords)
  • Elixir (1.10) (15 keywords)
  • Erlang (23) (27 keywords)
  • Fortran (Fortran 2008) (103 keywords)
  • Go (1.18) (25 keywords)
  • Java (SE 11 LTS) (51 keywords)
  • Java (SE 17 LTS) (67 keywords)
  • JavaScript (1st edition) (35 keywords)
  • JavaScript (2nd edition) (59 keywords)
  • JavaScript (3rd edition) (59 keywords)
  • JavaScript (5th edition) (45 keywords)
  • JavaScript (6th edition) (46 keywords)
  • Kotlin (1.4) (79 keywords)
  • Lua (5.3) (22 keywords)
  • MATLAB (R2020a) (20 keywords)
  • Objective-C (2.0) (85 keywords)
  • PHP (7.4) (69 keywords)
  • Python 2 (2.7) (31 keywords)
  • Python 3 (3.10) (38 keywords)
  • R (4.0) (21 keywords)
  • Ruby (2.7) (41 keywords)
  • Rust (1.46) (53 keywords)
  • Scala (2.13) (40 keywords)
  • Swift (5.3) (97 keywords)
  • Visual Basic (2019) (217 keywords)

从这个数据中看:Go只有25个关键字,跟C# 100+的关键字数量相比的确少了很多。这些关键字分别是

The following keywords are reserved and may not be used as identifiers.

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

如果只是看这些关键字其实也平平无奇,但是仔细看会发现其中并没有包含我们非常感兴趣、比较特殊、并且经常使用的make、new这两个关键字,尽管很多Go的介绍中都会提到这两个操作。

这两个操作符另一个重要的特点它们接受的参数是类型而不是数值,它的语法类似于C++中的模板。在编译器中,表达式和类型是两种不同的语法类型,需要做特殊处理。

Built-in functions

如果再找找看会发现,make/new(以及一些其他的)“关键字”是被列入到了Built-in functions这个分类中。

Built-in functions are predeclared. They are called like any other function but some of them accept a type instead of an expression as the first argument.

The built-in functions do not have standard Go types, so they can only appear in call expressions; they cannot be used as function values.

答案

StackOverflow上的合理解释

In short: because using predeclared identifiers in your own declarations don't make your code ambiguous.

回答者提供的示例代码

make := func() string {
    return "hijacked"
}

int := make()    // Completely OK, variable 'int' will be a string
fmt.Println(int) // Prints "hijacked"

讨论列表

官方作者的部分解释也从另一个侧面说明了make/new是两个比较特殊的函数

When we fixed up the grammar to avoid needing
a symbol table during the parse, one of my goals
was to make make and new not special, exactly
so that we could pass types to other things in
the future, like the case you've mentioned or some
unspecified form of generics.

gcc视角

为什么编译器可以声明而用户程序不能

简言之,编译器跳过了语法解析,直接将函数声明“注入”到了符号表中,而用户定义的函数必须经过编译器的语法解析,编译器不提供该语法功能就是无法实现。

可以看到,在函数声明中还声明了函数变参,也就是可以接受任意多参数。

// Class Gogo.

Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
  : backend_(backend),
  ///...
    const Location loc = Linemap::predeclared_location();

  Named_type* uint8_type = Type::make_integer_type("uint8", true, 8,
						   RUNTIME_TYPE_KIND_UINT8);
  this->add_named_type(uint8_type);
  this->add_named_type(Type::make_integer_type("uint16", true,  16,
					       RUNTIME_TYPE_KIND_UINT16));
  this->add_named_type(Type::make_integer_type("uint32", true,  32,
					       RUNTIME_TYPE_KIND_UINT32));
  this->add_named_type(Type::make_integer_type("uint64", true,  64,
					       RUNTIME_TYPE_KIND_UINT64));

  this->add_named_type(Type::make_integer_type("int8",  false,   8,
					       RUNTIME_TYPE_KIND_INT8));
  this->add_named_type(Type::make_integer_type("int16", false,  16,
					       RUNTIME_TYPE_KIND_INT16));
  Named_type* int32_type = Type::make_integer_type("int32", false,  32,
						   RUNTIME_TYPE_KIND_INT32);
  this->add_named_type(int32_type);
  this->add_named_type(Type::make_integer_type("int64", false,  64,
					       RUNTIME_TYPE_KIND_INT64));

  this->add_named_type(Type::make_float_type("float32", 32,
					     RUNTIME_TYPE_KIND_FLOAT32));
  this->add_named_type(Type::make_float_type("float64", 64,
					     RUNTIME_TYPE_KIND_FLOAT64));
///...
  this->globals_->add_constant(Typed_identifier("true",
						Type::make_boolean_type(),
						loc),
			       NULL,
			       Expression::make_boolean(true, loc),
			       0);
  this->globals_->add_constant(Typed_identifier("false",
						Type::make_boolean_type(),
						loc),
			       NULL,
			       Expression::make_boolean(false, loc),
			       0);

  this->globals_->add_constant(Typed_identifier("nil", Type::make_nil_type(),
						loc),
			       NULL,
			       Expression::make_nil(loc),
			       0);

  Type* abstract_int_type = Type::make_abstract_integer_type();
  this->globals_->add_constant(Typed_identifier("iota", abstract_int_type,
						loc),
			       NULL,
			       Expression::make_iota(),
			       0);

  Function_type* new_type = Type::make_function_type(NULL, NULL, NULL, loc);
  new_type->set_is_varargs();
  new_type->set_is_builtin();
  this->globals_->add_function_declaration("new", NULL, new_type, loc);

  Function_type* make_type = Type::make_function_type(NULL, NULL, NULL, loc);
  make_type->set_is_varargs();
  make_type->set_is_builtin();
  this->globals_->add_function_declaration("make", NULL, make_type, loc);
  ///...
}

make语义

编译器内部对于该调用的类型和意义相当于都做了处理。

///@file: gcc\go\gofrontend\expressions.cc
// Lower a make expression.

Expression*
Builtin_call_expression::lower_make(Statement_inserter* inserter)
{
  Location loc = this->location();

  const Expression_list* args = this->args();
  if (args == NULL || args->size() < 1)
    {
      this->report_error(_("not enough arguments"));
      return Expression::make_error(this->location());
    }

  Expression_list::const_iterator parg = args->begin();

  Expression* first_arg = *parg;
  if (!first_arg->is_type_expression())
    {
      go_error_at(first_arg->location(), "expected type");
      this->set_is_error();
      return Expression::make_error(this->location());
    }
  Type* type = first_arg->type();

  bool is_slice = false;
  bool is_map = false;
  bool is_chan = false;
  if (type->is_slice_type())
    is_slice = true;
  else if (type->map_type() != NULL)
    is_map = true;
  else if (type->channel_type() != NULL)
    is_chan = true;
  else
    {
      this->report_error(_("invalid type for make function"));
      return Expression::make_error(this->location());
    }

  Type_context int_context(Type::lookup_integer_type("int"), false);

  ++parg;
  Expression* len_arg;
  bool len_small = false;
  if (parg == args->end())
    {
      if (is_slice)
	{
	  this->report_error(_("length required when allocating a slice"));
	  return Expression::make_error(this->location());
	}
      len_arg = Expression::make_integer_ul(0, NULL, loc);
    }
  else
    {
      len_arg = *parg;
      len_arg->determine_type(&int_context);
      if (!this->check_int_value(len_arg, true, &len_small))
	return Expression::make_error(this->location());
      ++parg;
    }

  Expression* cap_arg = NULL;
  bool cap_small = false;
  if (is_slice && parg != args->end())
    {
      cap_arg = *parg;
      cap_arg->determine_type(&int_context);
      if (!this->check_int_value(cap_arg, false, &cap_small))
	return Expression::make_error(this->location());

      Numeric_constant nclen;
      Numeric_constant nccap;
      unsigned long vlen;
      unsigned long vcap;
      if (len_arg->numeric_constant_value(&nclen)
	  && cap_arg->numeric_constant_value(&nccap)
	  && nclen.to_unsigned_long(&vlen) == Numeric_constant::NC_UL_VALID
	  && nccap.to_unsigned_long(&vcap) == Numeric_constant::NC_UL_VALID
	  && vlen > vcap)
	{
	  this->report_error(_("len larger than cap"));
	  return Expression::make_error(this->location());
	}

      ++parg;
    }

  if (parg != args->end())
    {
      this->report_error(_("too many arguments to make"));
      return Expression::make_error(this->location());
    }

  Location type_loc = first_arg->location();

  Expression* call;
  if (is_slice)
    {
      Type* et = type->array_type()->element_type();
      Expression* type_arg = Expression::make_type_descriptor(et, type_loc);
      if (cap_arg == NULL)
	{
	  Temporary_statement* temp = Statement::make_temporary(NULL,
								len_arg,
								loc);
	  inserter->insert(temp);
	  len_arg = Expression::make_temporary_reference(temp, loc);
	  cap_arg = Expression::make_temporary_reference(temp, loc);
	  cap_small = len_small;
	}

      Runtime::Function code = Runtime::MAKESLICE;
      if (!len_small || !cap_small)
	code = Runtime::MAKESLICE64;
      call = Runtime::make_call(code, loc, 3, type_arg, len_arg, cap_arg);
    }
  else if (is_map)
    {
      Expression* type_arg = Expression::make_type_descriptor(type, type_loc);
      call = Runtime::make_call(Runtime::MAKEMAP, loc, 4, type_arg, len_arg,
				Expression::make_nil(loc),
				Expression::make_nil(loc));
    }
  else if (is_chan)
    {
      Expression* type_arg = Expression::make_type_descriptor(type, type_loc);
      call = Runtime::make_call(Runtime::MAKECHAN, loc, 2, type_arg, len_arg);
    }
  else
    go_unreachable();

  return Expression::make_unsafe_cast(type, call, loc);
}

覆盖(shadow)

回到最开始的问题:make不是关键字的原因在于它作为一个标识符是可以被覆盖的。

在添加的过程中,和C++编译器实现类似,也是包含了分层的Bindings中(函数是Bindings类的一个接口)。

///@file: gcc\go\gofrontend\gogo.cc
// Add a generic Named_object to a Contour.

Named_object*
Bindings::add_named_object_to_contour(Contour* contour,
				      Named_object* named_object)
{
  go_assert(named_object == named_object->resolve());
  const std::string& name(named_object->name());
  go_assert(!Gogo::is_sink_name(name));

  std::pair<Contour::iterator, bool> ins =
    contour->insert(std::make_pair(name, named_object));
  if (!ins.second)
    {
      // The name was already there.
      if (named_object->package() != NULL
	  && ins.first->second->package() == named_object->package()
	  && (ins.first->second->classification()
	      == named_object->classification()))
	{
	  // This is a second import of the same object.
	  return ins.first->second;
	}
      ins.first->second = this->new_definition(ins.first->second,
					       named_object);
      return ins.first->second;
    }
  else
    {
      // Don't push declarations on the list.  We push them on when
      // and if we find the definitions.  That way we genericize the
      // functions in order.
      if (!named_object->is_type_declaration()
	  && !named_object->is_function_declaration()
	  && !named_object->is_unknown())
	this->named_objects_.push_back(named_object);
      return named_object;
    }
}

疑问

基于类型的运行时分配,除了make/new这种操作还可以有其他的实现方式吗?如果没有,那么make/new是不是从逻辑上说就是一个语言不可缺少的一部分:也就是从逻辑上说应该算作这个语言的关键字?

标签:loc,type,make,关键字,keywords,arg,go,Expression
From: https://www.cnblogs.com/tsecer/p/17822770.html

相关文章

  • Leangoo领歌免费敏捷工具中如何看到关于自己的所有任务?
    ​个人工作台个人工作台是个人最新待办工作的展示区域,它展示了个人所有的待办任务,最新访问的项目和工作动态,当一个人在多个项目和看板上工作时,它可以帮助个人快速看到个人在各个项目的工作,快速进入任务看板处理任务。 ​www.leangoo.com​......
  • python中 “instance”关键字的作用
    在Python中,尤其是在使用Django框架时,“instance=”参数通常与表单(Forms)和模型(Models)操作相关。在Django表单系统中,这个参数经常被用于以下两种情况:在表单初始化时填充数据:当你创建一个ModelForm的实例并传递一个模型实例给instance=参数时,表单将用模型实例的数据填充它的字段。......
  • MongoDB的安装
    导航目录导航一、下载MongoDB二、创建目录以及配置文件(3个文件夹、2个文件)二、执行配置文件三、启动MongoDB一、下载MongoDB以下是安装MongoDB的安装配置,参考连接有两个mongodb5安装配置+设置自动启动(最新最简单的安装、配置方法)MongoDB解决“ErrorparsingYAMLconfigf......
  • Rust之cargo简单熟悉
    Rust之cargo简单熟悉还记得上一篇文章–《Rust简单开发环境搭建》中,helloworld的例子是用cargo来管理的,今天我们就来聊聊这个cargocargo是什么?为什么需要这个cargo?cargo是Rust的包管理器,Rust的包分为2种,一种是二进制可执行的包,一种是库的包,默认情况下就是第一种binary包在Rust里......
  • (十二)C#编程基础复习——break、continue、goto:跳出循环
    在使用循环语句时,并不是必须等待循环完成后才能退出循环,我们也可以主动退出循环,C#为我们提供了break、continue和goto三种方式来跳出循环:1、break它不仅可以用来终止switch语句,在循环语句中使用时还可以用来跳出循环,执行循环外的下一条语句。如果是在嵌套循环中使用,例如在内层的......
  • 【Django】使用gunicorn部署,找不到静态文件(admin,swagger...)
    先收集静态文件#settings.py里面需要指定收集的路径STATIC_ROOT与STATIC_URLpythonmanage.pycollectstatic添加识别代码#urls.pypath(r'^static/(?P<path>.*)$',serve,{'document_root':STATIC_ROOT}),......
  • Go Web开发进阶项目实战-Go语言实战课程体系,企业项目开发经验与技巧
    书接上回,上次我们搭建好了项目入口文件,同时配置了路由体系,接着就可以配置项目的模板了,这里我们采用Iris内置的模板引擎,事实上,采用模板引擎并不意味着前后端耦合,模板中的数据保持其独立性即可,也就是说模板的数据操作交互方式采用http接口请求的形式,Iris并不参与模板逻辑,只返回Jso......
  • m基于Yolov2和GoogleNet深度学习网络的疲劳驾驶检测系统matlab仿真,带GUI界面
    1.算法仿真效果matlab2022a仿真结果如下:  疲劳状态:    2.算法涉及理论知识概要      疲劳驾驶检测系统是一种基于深度学习网络的系统,它结合了Yolov2和GoogleNet模型,用于检测驾驶员的疲劳状态和人脸。疲劳驾驶检测系统主要包括两个部分:人脸检测和疲劳......
  • 使用 VSCode+CMake+Ninja 开发RISC-V MCU
    1.安装软件及工具1.1VSCode安装VisualStudionCode(VSCode),是一款由微软开发且跨平台的免费源代码编辑器。该软件支持语法高亮、代码自动补全(又称IntelliSense)、代码重构、查看定义功能,并且内置了命令行工具和Git版本控制系统。VSCode官网VSCode官方文档官网......
  • mongodb分配内存
    环境:Os:Centos7mongodb:4.4.22 配置文件设置的内存大小[root@localhostconf]#moremongo.cnfnet:bindIp:192.168.1.108,127.0.0.1port:28001storage:journal:enabled:truedbPath:"/home/middle/mongodb/data"engine:wiredTigerwiredTi......