> For the complete documentation index, see [llms.txt](https://mashplant.gitbook.io/decaf-doc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mashplant.gitbook.io/decaf-doc/pa2/visitor-mo-shi.md).

# visitor模式

在找一个类似于`Visitor`之类的trait吗?没有的，别想了。

rust中当然也可以有visitor模式这样的东西，比如<https://github.com/rust-unofficial/patterns/blob/master/patterns/visitor.md> 中给出的例子，不过我认为把`Visitor`像这个文章里一样作为一个明确的trait写出来，一是没有必要，二是限制了灵活性。

先说为什么没有必要。对于java或者是c++的面向对象子集那样的面向对象的语言，ast节点往往表示成继承的关系，例如`Call`继承自`Expr`之类的。如果不写类似于`Visitor`的interface或者是抽象类的话，要想判断并处理不同类型的ast节点，也许只能使用动态的类型判断/转换，也就是java中的`instanceof`+强制类型转换，c++中的`dynamic_cast`。以前的文档(不确定现在还有没有)称这是"一种比较龌龊但确实可行的方法"，其实我个人觉得这并没有什么道理，这并不比visitor模式更不优雅一些，尤其是考虑到类似scala等基于jvm但支pattern matching的语言，本质上pattern matching也是被编译器翻译成`instanceof`+强制类型转换。

总之，对于rust的enum来说，`match`是它最自然也最直接的使用方式，不必有任何心理负担，毕竟即使写了个visitor，里面实现的时候不也只能用`match`吗。

大家会发现代码中有一些非常巨大的函数，例如把所有对于`Expr`的处理全部放在了一个函数里面。我认为这并不会造成什么困扰，因为各个`match`的分支之间是独立的，把它们看成不同的函数也可以，但是我并不认为如果把它真的拆分成不同的函数，在可读性上比现在会有任何的优越性。

再说灵活性。一个trait/interface/抽象类都限定了函数的类型，这对我们来说是完全是没有意义的约束，会带来很多麻烦。例如函数的返回值可能必须为空，那么为了表示visitor从这个节点中获取的信息，就必须把信息存在节点里面，访问完后再取出来；例如函数只能接受节点作为输入参数，那么为了传递一些临时的状态，就必须把这个状态作为struct/class的一个成员。

有人可能认为新增了一种ast节点之后就会出现很多编译错误，这是不灵活的表现，而如果用visitor的话只要在trait里加几个默认的空函数即可。对此我的看法是，编译错误本来就不是坏事，它直接就可以提醒你哪些地方需要修改，这并不比默默的编译通过了但是结果不对要差。如果修改某个地方的工作量的确比较大，又想尽快测试已经修改好的部分，那么填上几个`unimplemented!()`即可。

不写visitor也的确有一些劣势，例如为了判断节点类型，match在每个地方都得出现一次，产生了一些重复的代码。这是个取舍的问题，我个人不觉得这是很大的负担。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://mashplant.gitbook.io/decaf-doc/pa2/visitor-mo-shi.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
