Info
This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Projections that do not decide mutability up front are possible
I encountered the following problem while working on a transpiler from non-Rust code to Rust. Suppose that I have these structs:
#[derive(Debug, bytemuck::TransparentWrapper)]
#[repr(transparent)]
struct Foo(pub i32);
struct Bar {
pub x: i32,
pub y: i32,
}
Suppose that I want to pretend that Bar
has fields of type Foo
as much as it has fields of type i32
, that is, be able to write an expression that:
x
or y
of a Bar
, in the same way that bar.x
and bar.y
do,Foo
, not i32
(which is sound because of the repr(transparent)
), andAfter presuming for a while that this is impossible (after all, conventional Rust APIs have all these separate *_ref()
and *_mut()
functions, which must be there for a reason), I realized today that there is in fact an overloadable operator in Rust that offers this opportunity: the indexing operator.
use std::ops;
use bytemuck::TransparentWrapper;
struct X;
struct Y;
impl ops::Index<X> for Bar {
type Output = Foo;
fn index(&self, X: X) -> &Self::Output {
TransparentWrapper::wrap_ref(&self.x)
}
}
impl ops::Index<Y> for Bar {
type Output = Foo;
fn index(&self, Y: Y) -> &Self::Output {
TransparentWrapper::wrap_ref(&self.y)
}
}
impl ops::IndexMut<X> for Bar {
fn index_mut(&mut self, X: X) -> &mut Self::Output {
TransparentWrapper::wrap_mut(&mut self.x)
}
}
impl ops::IndexMut<Y> for Bar {
fn index_mut(&mut self, Y: Y) -> &mut Self::Output {
TransparentWrapper::wrap_mut(&mut self.y)
}
}
These definitions let this sort of code compile:
fn simple_example() {
let mut bar = Bar { x: 0, y: 0 };
bar[X] = Foo(1);
bar[Y] = Foo(2);
println!("{:?} {:?}", bar[X], bar[Y]);
}
fn more_specific_borrow_checked_usage(input: &mut Bar) {
// Can have multiple borrows (must not use `&mut input`)
let x1 = &input[X];
let x2 = &input[X];
println!("{x1:?}, {x2:?}");
// Can mutate (must not use `&input`)
let x3 = &mut input[X];
*x3 = Foo(10);
assert_eq!(input.x, 10);
}
It will even work on foreign types:
impl ops::Index<X> for [i32; 2] {
type Output = Foo;
fn index(&self, X: X) -> &Self::Output {
TransparentWrapper::wrap_ref(&self[0])
}
}
// ...
I haven't yet used this trick (I only just thought of it while I was cleaning out some old draft posts) and I may or may not actually use it, but I thought it was worth sharing.
2 posts - 1 participant
🏷️ rust_feed