我有一个可以定义整数类型的小库.这些是为了在我经常编写的算法中对数组和字符串进行类型安全的索引.例如,我可以用它来定义一个偏移类型Offset和一个索引类型Idx,这样你可以通过减go 两个Idx得到一个Offset,你可以通过加或减Offset得到Idx,但是你不能,例如,乘以或加Idx.

let (i,j): (Idx,Idx) = ...;
let offset: Offset = j - i; // Subtracting indices gives an offset
let k: Idx = j + offset; // Adding an offset to an index gives an index
// let _ = i + j; -- You can't add indices

我设法(with some difficulty)实现了std::iter::Step,因此我还可以迭代一系列索引

for k in i .. j { /* ... */ }

但现在我已经着眼于一个更高的目标:我还想使用这些类型的范围来分割序列

let v: Vec<sometype> = vec![...];
let w: &[sometype] = &v[i..j]; // Slice with a range of Idx

这应该是实现std::ops::Indexstd::ops::IndexMut的简单问题,只是类型系统不允许我实现

impl<I,T> std::ops::Index<std::ops::Range<I>> for Vec<T>
where I: /* my types */
{ ... }

对于包装器类型或泛型

impl<I,T> std::ops::Index<std::ops::Range<Wrapper<I>>> for Vec<T>
where I: /* my types */
{ ... }

其中Wrapper是我实际使用的类型,I是帮助我编写通用代码的特征.

问题是IndexRange都是在我的 crate 之外定义的,所以这种专业化是不允许的.还是这样?

当泛型类型的泛型参数来自于我的 crate 时,有什么方法可以在我的 crate 之外实现泛型类型的特征吗?

或者,更好的是,有没有办法利用..运算符的语法糖分,这样我就可以得到一个包装类型?我想要的是包装Range,以获得相同的行为,然后一些.我可以通过包装Range和实现Deref来实现这一点,但如果我这样做,我就失go 了..操作符的语法优势.

这不是一个大问题,但我可以想象当你会写作时会有一些困惑

for k in i .. j { /* ... */ }

就像内置类型一样,但必须使用

let w: &[type] = &v[range(i,j)];

用于切片.如果我想让像i....j这样的切片包起来,那就更麻烦了.(..片在这里无关紧要,反正也不会得到我想要的类型).如果我这样做了,我将需要三种类型的范围的构造函数,或者一些丑陋的包装使用Option,我想.

..语法糖真的很好,但从我探索的内容来看,你不能对自己类型的范围使用那么多.你可以定义范围,通过一些黑客攻击,你可以迭代它们,但你不能用它们建立索引.

告诉我我错了,或者让我知道是否有什么技巧可以完成工作,甚至完成一半.或者,如果这确实不可能,请告诉我,这样我就可以停止在上面浪费时间,编写一个包装器类,放弃..运算符.

Update我在playgrounds中举了一个简化的例子

推荐答案

不,你不能.

根据orphan rules人的定义:

给定impl<P1..=Pn> Trait<T1..=Tn> for T0impl仅在以下至少一项为真时有效:

只有uncovered个类型参数的外观受到限制.注意,为了连贯性,fundamental types是特殊的.T-in-Box不被认为是覆盖的,而Box被认为是局部的.

地方特色

当前 crate 中定义的trait.特征定义是局部的,或者与应用的类型参数无关.给定trait Foo<T, U>Foo总是本地的,不管TU的类型是什么.

本地类型

当前 crate 中定义的structenumunion.这不受应用类型参数的影响.struct Foo被认为是本地的,但Vec<Foo>不是.LocalType<ForeignType>是本地的.类型别名不影响位置.

因为IndexRangeVec都不是本地类型,Range也不是基本类型,所以你不能用impl<T> Index<Range<...>> for Vec<T>来代替....

这些规则的原因是没有任何东西阻止RangeVec实现impl<T, Idx> Index<Range<Idx>> for Vec<T>.这样的impl不存在,可能永远也不会存在,但规则在所有类型中都是相同的,在一般情况下,这肯定会发生.

您也不能重载range操作符——它总是创建一个Range(或RangeInclusiveRangeFull等).

我能想到的唯一解决方案是按照 comments 中的建议,为Vec创建一个新的类型包装器.

如果希望向量返回一个已包装的切片,可以使用一些不安全的代码:

use std::ops::{Index, IndexMut, Range, Deref, DerefMut};

#[repr(transparent)] // Because of this we can soundly cast `&{mut }[T]` to `&{mut }MySlice<T>`.
pub struct MySlice<T>([T]);

impl<'a, T> From<&'a [T]> for &'a MySlice<T> {
    fn from(v: &'a [T]) -> &'a MySlice<T> {
        unsafe { &*(v as *const [T] as *const MySlice<T>) }
    }
}
impl<'a, T> From<&'a mut [T]> for &'a mut MySlice<T> {
    fn from(v: &'a mut [T]) -> &'a mut MySlice<T> {
        unsafe { &mut *(v as *mut [T] as *mut MySlice<T>) }
    }
}

impl<T> Index<usize> for MySlice<T> {
    type Output = T;
    fn index(&self, idx: usize) -> &Self::Output { &self.0[idx] }
}
impl<T> IndexMut<usize> for MySlice<T> {
    fn index_mut(&mut self, idx: usize) -> &mut Self::Output { &mut self.0[idx] }
}

impl<T> Index<Range<usize>> for MySlice<T> {
    type Output = MySlice<T>;
    fn index(&self, idx: Range<usize>) -> &Self::Output { self.0[idx].into() }
}
impl<T> IndexMut<Range<usize>> for MySlice<T> {
    fn index_mut(&mut self, idx: Range<usize>) -> &mut Self::Output { (&mut self.0[idx]).into() }
}
// And so on, for all range types...

pub struct MyVec<T>(pub Vec<T>);

impl<T> Deref for MyVec<T> {
    type Target = MySlice<T>;
    fn deref(&self) -> &Self::Target { self.0.as_slice().into() }
}
impl<T> DerefMut for MyVec<T> {
    fn deref_mut(&mut self) -> &mut Self::Target { self.0.as_mut_slice().into() }
}

Rust相关问答推荐

如何容器化Linux上基于Rust的Windows应用程序的编译过程?

展开枚举变量并返回所属值或引用

在Rust中赋值变量有运行时开销吗?

在自定义序列化程序中复制serde(With)的行为

交换引用时的生命周期

使用极点数据帧时,找不到枚举结果的方法lazy()

我可以在不收集或克隆的情况下,将一个带有Item=(key,val)的迭代器拆分成单独的key iter和val iter吗?

考虑到Rust不允许多个可变引用,类似PyTorch的自动区分如何在Rust中工作?

为什么BufReader实际上没有缓冲短寻道?

Rust Axum 框架 - 解包安全吗?

`use` 和 `crate` 关键字在 Rust 项目中效果不佳

没有得到无法返回引用局部变量`queues`的值返回引用当前函数拥有的数据的值的重复逻辑

`移动||异步移动{...}`,如何知道哪个移动正在移动哪个?

如何使返回 XMLError 的方法与 anyhow::Error 兼容?

如何在 Rust 中返回通用 struct

在 FFI 的上下文中,未初始化是什么意思?

如何存储返回 Future 的闭包列表并在 Rust 中的线程之间共享它?

通用函数中的生命周期扣除和borrow (通用测试需要)

在 Rust 中退出进程

如何将 while 循环内的用户输入添加到 Rust 中的向量?