Go转Rust:Parser的new方法实现是否为最优写法?
Go转Rust:Parser构造函数的实现优化建议
问题描述
我有一段Go语言的Parser构造函数代码,希望将其改写为Rust语言:
func New(l *lexer.Lexer) *Parser { p := &Parser{l: l} // Read two tokens, so curToken and peekToken // are both set p.nextToken() p.nextToken() return p } func (p *Parser) nextToken() { p.curToken = p.peekToken p.peekToken = p.l.NextToken() }
我目前写出的Rust代码如下:
struct Parser { l: lexer::Lexer, cur_token: lexer::Token, peek_token: lexer::Token, } impl Parser { fn new(l: lexer::Lexer, cur_token: lexer::Token, peek_token: lexer::Token) -> Self { Self { l: l, cur_token: cur_token, peek_token: peek_token, } } fn init(&mut self) { self.next_token(); self.next_token(); } fn next_token(&mut self) { self.cur_token = self.peek_token; self.peek_token = self.l.next_token(); } }
请问这段Rust代码是否是实现该功能的最佳方式?
回答
你当前的Rust实现并不贴合原Go代码的设计意图,也不是最优写法,核心问题有两点:
new方法要求调用者传入cur_token和peek_token,但原Go逻辑是构造Parser时自动完成这两个字段的初始化,不需要调用者关心内部的token预读细节- 额外拆分的
init方法增加了使用成本,调用者必须记得手动调用才能让Parser处于可用状态,容易引发遗漏初始化的错误
更贴合原逻辑且符合Rust习惯的实现,应该让new方法自己完成所有初始化工作,无需暴露额外步骤:
struct Parser { l: lexer::Lexer, cur_token: lexer::Token, peek_token: lexer::Token, } impl Parser { fn new(mut l: lexer::Lexer) -> Self { // 先获取第一个token作为peek_token初始值 let peek_token = l.next_token(); let mut parser = Self { l, // 初始cur_token用peek_token的克隆值,第一次next_token会覆盖它 cur_token: peek_token.clone(), peek_token, }; // 调用两次next_token完成初始化,和Go逻辑完全对齐 parser.next_token(); parser.next_token(); parser } fn next_token(&mut self) { self.cur_token = self.peek_token; self.peek_token = self.l.next_token(); } }
如果Token类型不支持Clone,可以用Option<Token>处理初始值,避免不必要的克隆操作:
struct Parser { l: lexer::Lexer, cur_token: Option<lexer::Token>, peek_token: Option<lexer::Token>, } impl Parser { fn new(mut l: lexer::Lexer) -> Self { let mut parser = Self { l, cur_token: None, peek_token: None, }; parser.next_token(); parser.next_token(); parser } fn next_token(&mut self) { self.cur_token = self.peek_token.take(); self.peek_token = Some(self.l.next_token()); } }
修改后的实现优势:
- 调用者只需传入
lexer::Lexer就能得到完全初始化的Parser,和原Go代码的使用方式完全一致 - 无需额外的
init方法,消除了忘记初始化的风险 - 内部逻辑和原Go代码对齐,保持了相同的双token预读行为
内容的提问来源于stack exchange,提问作者learnlearnlearn
相关产品推荐
相关产品推荐

