英文:
In Rust, is there a way to make function accept two types without a trait?
问题
我对Rust还不太了解。我的问题更多是理论性的。问题是这样的。
我有一个状态机(用于通过quick-xml crate解析XML,但对于这个问题来说并不重要),其转换逻辑由一个match
表达式控制。表达式的许多分支具有额外的匹配保护条件。这些匹配保护条件大多相同,看起来像这样:
Event::Start(bs: BytesStart) if bs.local_name().as_ref() == b"any-tag" => NextState,
// 和
Event::End(be: BytesEnd) if be.local_name().as_ref() == b"any-tag" => SomeOtherState,
为了清理代码,我想将相等比较移到一个单独的函数中,如下所示:
fn tag_is(tag: &<要么是BytesStart要么是BytesEnd>, assertion: &str) -> bool {
tag.local_name().as_ref() == assertion.as_bytes()
}
// 并使用它
Event::Start(bs: BytesStart) if tag_is(&bs, "any-tag") => NextState,
BytesStart 和 BytesEnd 是不同的结构/类型,并且都有自己的 .local_name()
方法。如果.local_name()
定义在某个trait(TraitWithLocalName
)中,并且为两者都实现了,那么我可以编写一个泛型函数如下:
fn tag_is(tag: &impl TraitWithLocalName, assertion: &str) -> bool {...}
所以,问题是,如何处理这种情况 - 当一个函数可能接受不同类型的参数,这些参数没有共享相同的trait呢?
英文:
I am pretty new to Rust. My question is more theoretical. The problem is this.
I have a state machine (to parse XML via quick-xml crate but this is not important for this question) whose transition logic is controlled by a match
expression. A lot of branches of the expression have additional match guards. Most of those match guards are pretty identical and look like:
Event::Start(bs: BytesStart) if bs.local_name().as_ref() == b"any-tag" => NextState,
// and
Event::End(be: BytesEnd) if be.local_name().as_ref() == b"any-tag" => SomeOtherState,
To clean up the code I would like to move that equality comparison to a separate function, like:
fn tag_is(tag: &<either BytesStart or BytesEnd>, assertion: &str) -> bool {
tag.local_name().as_ref() == assertion.as_bytes()
}
// and use it
Event::Start(bs: BytesStart) if tag_is(&bs, "any-tag") => NextState,
BytesStart and BytesEnd are different structs/types, and both have their own .local_name()
methods. If the .local_name()
was defined in some trait (TraitWithLocalName
) and implemented for both, then I would write a generic like:
fn tag_is(tag: &impl TraitWithLocalName, assertion: &str) -> bool {...}
So, the question is, how to deal with that kind of situation - when a function might accept various types of an argument, which don't share the same trait?
答案1
得分: 3
你可以编写自己的 trait,其中包含这个函数,并为 BytesStart
和 BytesEnd
都实现它。
英文:
You can write your own trait that has this function and implement it for both BytesStart
and BytesEnd
.
答案2
得分: 0
你不能用一个函数来做这个。如果你真的想的话,可以尝试用宏来实现。我强烈建议定义自己的特性来完成这个任务,但这取决于你。
macro_rules! tag_is {
($x: expr, $assertion: expr) => {{
let assertion: &str = $assertion; // 类型检查 - 确保 assertion 是一个字符串。
let s: &[u8] = $x.local_name().as_ref();
s == assertion.as_bytes()
}}
}
struct BytesStart;
impl BytesStart {
fn local_name(&self) -> &str {
"hello"
}
}
struct BytesEnd;
impl BytesEnd {
fn local_name(&self) -> &[u8] {
b"any-tag"
}
}
pub fn main() {
println!("{}", tag_is!(BytesStart, "any-tag")); // false
println!("{}", tag_is!(BytesEnd, "any-tag")); // true
}
英文:
You cannot do this with a function. If you really wanted to, you could try doing it with a macro. I would strongly prefer defining my own trait to doing things this way, but it's up to you.
macro_rules! tag_is {
($x: expr, $assertion: expr) => {{
let assertion: &str = $assertion; // type checking - makes sure assertion is a str.
let s: &[u8] = $x.local_name().as_ref();
s == assertion.as_bytes()
}}
}
struct BytesStart;
impl BytesStart {
fn local_name(&self) -> &str {
"hello"
}
}
struct BytesEnd;
impl BytesEnd {
fn local_name(&self) -> &[u8] {
b"any-tag"
}
}
pub fn main() {
println!("{}", tag_is!(BytesStart, "any-tag")); // false
println!("{}", tag_is!(BytesEnd, "any-tag")); // true
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论