Struct Solution

Source
pub struct Solution;
Expand description

用有限状态机,处理几个有限的状态就解决了。

Rust 的好处是很容易写比较高级的抽象。而且对错误处理非常友好。语法上主要是 Pattern Matching 和各种高阶函数的 combinator 组合。感觉用 Erlang/Elixir 写起来应该会很方便。

代码逻辑:

  1. chars() 生成字符串的 Iterator
  2. try_fold 消费这个 Iterator,创建一个新的 Validator 对象作为 fold 的初始状态传入。
  3. try_fold 的每一次回调处理一个字符,调用 validator.handle(c) 返回一个 Result。 只要每步都 Ok,那就一直 handle 下去,只要中间发现 Err,就会提早返回 (短路操作)
  4. 最后消费完 Iterator 也就是所有字符都处理完了,需要判断下 Validator.is_end() 是否合法结束,即检查 Validator 的状态是否是 End(完全闭合最外层的标签)。

主要是 Validator 里的几个状态定义:

  • Init 初始状态,只接收 < 就进入 TagName 状态,否则失败
  • TagName 这个状态比较复杂,其实就是 Tag 的定义条件,这里用了一个 is_close 来标识是不是 关闭标签
  • TagContent 这个状态简单,只要遇到 < 就进入 TagName 状态,其它都 Ok
  • CDataTag 这个也只有一种情况,读取完整的 [CDATA[ 进入 CDataContent 状态,否则失败
  • CDataContent 这个状态也简单(用了两个 bool 标记是否已经有连续两个 ] ),遇到 ]]> 就结束,回到 TagContent 状态(注意 reset 标记)
  • End 最外层的 Tag 闭合,结束,不能再有内容(即如果这个状态下再接收到内容,都是 Err)

这里就 TagName 里处理得最多,具体条件结合题目定义,看代码。

另外,Validator 里定义了一个 stack,记录 开标签,用于与 闭标签 匹配。

Rust 的类型系统使得错误处理机制非常严谨,安全。代码里每一个地方都记录了 invalid 的错误信息,可以在最外层调用的地方统一打印。

因为用 Iterator,是 lazy 调用,只有在每一步迭代的时候才会执行代码,只迭代了一遍。(如果要调试输入的情况,在 try_fold 前面加一个 inspect 可以打印出每个字符,结合每个地方的状态变化,可以很轻松地调试)。 因为代码抽象程度比较好,虽然看起来代码长了一点,但结构非常清晰,可读性,安全性,可扩展/维护性都很优秀。

Implementations§

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.