首页 > 解决方案 > 允许针对不同版本的源代码互操作的确切机制是什么?

问题描述

我了解 Editions 的目标以及如何使用它们,但文档并没有说明它们的内部工作原理。

假设我有两个源文件:

我想构建一个同时使用它们的应用程序。使用什么机制来确保它们可以互操作?

两者都old.rs翻译new.rs成相同的 HIR 模型?或者均质化发生在之前(例如 AST 水平)还是之后(例如 MIR 水平)?

标签: rust

解决方案


这些版本只影响语法,不会改变编译器编译代码的方式。解析器几乎是编译器中唯一支持版本的组件,需要对版本进行一些检查以决定如何解析内容。两个版本的 AST 是相同的,尽管 spans 知道它们的版本并且编译器中的一些东西会检查它们使用的是哪个版本。HIR 和 MIR 不需要知道版本。

例如。对于新关键字

/// Returns `true` if the token is a keyword used in the language.
    pub fn is_used_keyword(self) -> bool {
    // Note: `span.edition()` is relatively expensive, don't call it unless necessary.
    self.name >= kw::As && self.name <= kw::While ||
    self.name.is_used_keyword_2018() && self.span.rust_2018()
}

extraself.name.is_used_keyword_2018() && self.span.rust_2018()会检查该关键字是否是2018 年版本中添加的关键字(例如。dyn),对于2015 年,它将被视为常规符号。

另一个例子是,在 2015 年,可以在 trait 声明中省略参数名称,现在这是被禁止的。这也是透明处理的

// We don't allow argument names to be left off in edition 2018.
let is_name_required = p.token.span.rust_2018();
p.parse_arg_general(true, false, |_| is_name_required)

然后在这种情况下,另一段代码将发出错误,但仅适用于 2018

if require_name && (
    is_trait_item ||
    self.token == token::Comma ||
    self.token == token::CloseDelim(token::Paren)
) { // `fn foo(a, b) {}` or `fn foo(usize, usize) {}`
    err.span_suggestion(
        pat.span,
        "if this was a parameter name, give it a type",
        format!("{}: TypeName", ident),
        Applicability::HasPlaceholders,
    );
    err.span_suggestion(
        pat.span,
        "if this is a type, explicitly ignore the parameter name",
        format!("_: {}", ident),
        Applicability::MachineApplicable,
    );
    err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)");
    return Some(ident);
}

而对于 2015 年,则会创建一个虚拟名称

let ident = Ident::new(kw::Invalid, self.prev_span);
let pat = P(Pat {
    id: ast::DUMMY_NODE_ID,
    node: PatKind::Ident(
        BindingMode::ByValue(Mutability::Immutable), ident, None),
    span: ty.span,
});

编译器的其余部分不需要知道用户是否实际提供了名称。


推荐阅读