Rust中判断一个range是否包含另一个range的惯用方法
Rust中判断一个range是否包含另一个range的惯用方法
我太懂这种别扭感了——你写的第一个测试用例完全符合直觉,但编译器就是不买账,因为contains方法本来就只接受单个元素,不是另一个Range。而你现在用的手动判断方法,那个-1不仅看着不优雅,还容易因为搞混Range的开闭规则出错。
先给你理清核心逻辑:Rust里的Range默认是左闭右开(start ≤ x < end),比如0..42包含0到41,但不包含42;而RangeInclusive(..=)是全闭区间,比如0..=42包含42本身。所以判断一个Range包含另一个Range的关键,是根据区间的开闭类型,比较两者的起始和结束边界。
针对普通左闭右开Range的简洁写法
如果只是处理a..b这种左闭右开的Range,其实完全不需要那个-1,直接比较边界就够了:
#[test] fn test_rangyness() { let a = 0..42; let b = 1..41; // 核心逻辑:a的起始 ≤ b的起始,且b的结束 ≤ a的结束 assert!(a.start <= b.start && b.end <= a.end); }
这个逻辑的本质是:只要b的所有元素都落在a的区间里,就满足包含关系。因为b是左闭右开,所以b的最大元素是b.end - 1,而a的区间包含到a.end - 1,只要b.end ≤ a.end,那b.end -1肯定小于a.end,自然被a包含。
兼容多种Range类型的通用方法
如果你的代码里还会用到RangeInclusive(全闭区间),可以写一个通用的辅助函数,自动处理不同的边界类型:
use std::ops::{Bound, RangeBounds}; /// 判断外层Range是否包含内层Range fn range_contains_range<Outer, Inner>(outer: Outer, inner: Inner) -> bool where Outer: RangeBounds<<Inner as RangeBounds>::Item>, Inner: RangeBounds<<Outer as RangeBounds>::Item>, <Outer as RangeBounds>::Item: PartialOrd, { // 获取外层区间的边界值 let outer_start = match outer.start_bound() { Bound::Included(s) => s, Bound::Excluded(s) => s, Bound::Unbounded => panic!("暂不支持无界区间"), }; let outer_end = match outer.end_bound() { Bound::Included(e) => e, Bound::Excluded(e) => e, Bound::Unbounded => panic!("暂不支持无界区间"), }; // 获取内层区间的边界值 let inner_start = match inner.start_bound() { Bound::Included(s) => s, Bound::Excluded(s) => s, Bound::Unbounded => panic!("暂不支持无界区间"), }; let inner_end = match inner.end_bound() { Bound::Included(e) => e, Bound::Excluded(e) => e, Bound::Unbounded => panic!("暂不支持无界区间"), }; // 检查起始边界:内层的起始不能超过外层的起始 let start_ok = match (outer.start_bound(), inner.start_bound()) { (Bound::Included(_), Bound::Included(_)) => inner_start <= outer_start, (Bound::Included(_), Bound::Excluded(_)) => inner_start < outer_start, (Bound::Excluded(_), Bound::Included(_)) => inner_start <= outer_start, (Bound::Excluded(_), Bound::Excluded(_)) => inner_start < outer_start, _ => false, }; // 检查结束边界:内层的结束不能超过外层的结束 let end_ok = match (outer.end_bound(), inner.end_bound()) { (Bound::Included(_), Bound::Included(_)) => inner_end <= outer_end, (Bound::Included(_), Bound::Excluded(_)) => inner_end <= outer_end, (Bound::Excluded(_), Bound::Included(_)) => inner_end < outer_end, (Bound::Excluded(_), Bound::Excluded(_)) => inner_end < outer_end, _ => false, }; start_ok && end_ok }
用这个函数就可以轻松处理各种区间组合:
#[test] fn test_mixed_ranges() { // 左闭右开包含左闭右开 let a = 0..42; let b = 1..41; assert!(range_contains_range(a, b)); // 全闭区间包含全闭区间 let c = 0..=42; let d = 1..=41; assert!(range_contains_range(c, d)); // 全闭区间包含左闭右开区间 let e = 0..=42; let f = 1..42; assert!(range_contains_range(e, f)); }
这样既符合Rust的惯用风格,又避免了手动处理边界的麻烦,还能兼容不同类型的区间。
备注:内容来源于stack exchange,提问作者Fee
相关产品推荐
相关产品推荐

