I have a simple struct Parser
我有一个简单的结构解析器
pub struct Parser {
pos: usize,
input: String,
}
I'm trying to build a markdown parser and there's a method parse_md_line
which contains a while loop and I'm trying to make the while loop's condition dynamic based on delimiter
value
我正在尝试构建一个markdown解析器,并且有一个包含While循环的方法parse_md_line,并且我正在尝试根据分隔符值动态设置While循环的条件
pub fn parse_md_line(&mut self, delimiter: Option<char>) -> String {
let mut result = String::new();
let mut cond: Box<dyn Fn() -> bool> = Box::new(|| {
return self.pos < self.input.len()
&& (self.next_char() != '\n' || utils::is_whitespace(self.next_char()));
});
if delimiter.is_some() {
cond = Box::new(|| {
return self.pos < self.input.len() && self.next_char() != delimiter.unwrap();
});
}
while cond() {
let normal_text = self.parse_alpha_numeric(true); // this mutates `self`
.....
}
result
}
while loop's inner working are hidden as they do matter much, but it calls some methods, which mutates self
.
虽然循环的内部工作是隐藏的,因为它们确实很重要,但它调用了一些方法,这会使自身发生突变。
This code throw error for let normal_text = self.parse_alpha_numeric(true);
,
**cannot borrow *self as mutable because it is also borrowed as immutable.
此代码引发错误,因为let Normal_Text=self.parse_Alpha_Numeric(True);,**不能借用*self作为可变对象,因为它也被借用为不可变对象。
I understand the error and why it's happening. But cannot figure out a way to work it out. I hit and tried using RefCell
, Rc
still nothing of use.
我理解这个错误以及为什么会发生这种情况。但想不出办法来解决这个问题。我点击并尝试使用Refcell,RC仍然没有任何用处。
Maybe It can be done using RefCell
, from what I've tried the code grew unnecessary complex.
也许这可以使用RefCell来完成,从我尝试的情况来看,代码变得不必要的复杂。
Is there a standard or cleaner way to handle things like this.
有没有一种标准的或更干净的方式来处理这样的事情。
更多回答
优秀答案推荐
Multiple issues here:
这里有多个问题:
Fn
can't modify it's environment, you could change it to FnMut
but
- you're creating multiple closures that want to hold a mutable reference to self simultaneously but
&mut self
can only be held once
- you want to modify
self
while the closure holds on to a exclusive reference, you can't create multiple exclusive references to self though.
To solve them you can make self
a parameter for the closure, that way it doesn't need to capture self
, or hold on to it across the loop body, it also doesn't need to be a FnMut
because you don't modify the environment any longer:
要解决这些问题,可以将self作为闭包的参数,这样它就不需要捕获self,或者在循环体中保留它,它也不需要是FnMut,因为您不再修改环境:
pub fn parse_md_line(&mut self, delimiter: Option<char>) {
let cond: Box<dyn Fn(&mut Self) -> bool> = if let Some(delimiter) = delimiter {
Box::new(move |this| this.pos < this.input.len() && this.next_char() != delimiter)
} else {
Box::new(|this| this.pos < this.input.len() && !this.next_char().is_whitespace())
};
while cond(self) {
let _normal_text = self.parse_alpha_numeric(true); // this mutates `self`
}
}
You can also just write the condition inline to avoid any allocation of a closure and the complete problem, the compiler is clever enough to optimize the if
check to ahead of the loop:
您也可以只内联编写条件,以避免任何闭包和完整问题的分配,编译器足够聪明,可以在循环之前优化if检查:
pub fn parse_md_line(&mut self, delimiter: Option<char>) {
while self.pos < self.input.len()
&& if let Some(delimiter) = delimiter {
self.next_char() != delimiter
} else {
!self.next_char().is_whitespace()
}
{
let _normal_text = self.parse_alpha_numeric(true); // this mutates `self`
}
}
Alternatively, you can avoid using methods of Self
in the closure.
或者,您可以避免在闭包中使用self方法。
pub fn parse_md_line(&mut self, delimiter: Option<char>) -> String {
let mut result = String::new();
let mut check_delimiter: Box<dyn Fn(char) -> bool> = match delimiter {
Some(delimiter) => Box::new(move |c| {
c != delimiter
}),
None => Box::new(|c| {
c != '\n' /* || utils::is_whitespace(self.next_char()) */
})
};
while self.pos < self.input.len() && check_delimiter(self.next_char()) {
let _normal_text = self.parse_alpha_numeric(true);
}
result
}
Also, with such an approach, you can avoid using the .unwrap()
in the code.
playground link.
此外,使用这种方法,您可以避免在代码中使用.unwork()。操场链接。
更多回答
我是一名优秀的程序员,十分优秀!