Below I have a function that could work on the same data as source
and destination
, or different ones. If you pass no source, it should treat the destination as the source.
use core::ops::IndexMut;
use core::ops::Index;
use core::marker::PhantomData;
struct B<'a, T>{
_phantom: PhantomData<&'a T>
}
struct BMut<'a, T>{
_phantom: PhantomData<&'a mut T>
}
impl<'a, T> Index<T> for B<'a, T> {
type Output = T;
fn index(&self, t: T) -> &T {
unimplemented!()
}
}
impl<'a, T> Index<T> for BMut<'a, T> {
type Output = T;
fn index(&self, t: T) -> &T {
unimplemented!()
}
}
impl<'a, T> IndexMut<T> for BMut<'a, T> {
fn index_mut(&mut self, t: T) -> &mut T {
unimplemented!()
}
}
impl<'a, T> From<BMut<'a, T>> for B<'a, T> {
fn from(_e: BMut<'a, T>) -> Self {
B{
_phantom: PhantomData
}
}
}
trait A where Self: Sized {
fn transform(source: Option<&B<Self>>, destination: &mut BMut<Self>);
}
impl A for u64 {
fn transform(source: Option<&B<Self>>, destination: &mut BMut<Self>) {
let source = match source{
Some(source) => source,
None => destination.into()
};
//do a transformation here that reads and writes from destination.
//In case we specified a source, then it uses it, otherwise uses destination
//as a source. Technically this should be possible because it's impossible to pass
//a source that is equal to destination
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=15a2cae5c1240890e6f611d79e74958f
The problem is that I cannot convert BMut
to B
. Yes, I could access BMut
in the function a
everytime I needed B
if the source was not specified, but would be much cooler to have one type for source and be able to set it as destination only once.
⣿
Standard Error
error[E0277]: the trait bound `&B<'_, u64>: From<&mut BMut<'_, u64>>` is not satisfied
--> src/lib.rs:49:33
|
49 | None => destination.into()
| ^^^^ the trait `From<&mut BMut<'_, u64>>` is not implemented for `&B<'_, u64>`
|
= help: the following implementations were found:
<B<'a, T> as From<BMut<'a, T>>>
= note: required because of the requirements on the impl of `Into<&B<'_, u64>>` for `&mut BMut<'_, u64>`
I think there's multiple issues here. The surface issue right now, from the compiler error, is that the conversion doesn't work, right? And the compiler tells you why. It knows how to turn an owned BMut
into an owned B
but it doesn't know how to do that for references.
Now in your playground example you're using phantom data, but I assume that in reality your structs contain (and own!) actual pieces of interesting data that doesn't implement the Copy
trait.
The problem then is that, to cheaply create a B
out of a BMut
you need to move those owned values into the new struct. That's what From
and Into
do, anyway. But you can't move things out of a shared reference.
Another issue: The borrow checker doesn't actually let you write a function that takes two separate mutable references as arguments which you then call with the same data. What I mean is:
fn some_operation(&mut source: SomeType, &mut dest: SomeType) {
dest.do_something_mutable(source.get_some_info())
}
fn main() {
let source_and_dest = SomeType::new();
some_operation(&mut source_and_dest, &mut source_and_dest); // ERROR!
}
The compiler will complain because now you're taking two mutable references of the same variable, which the borrow checker doesn't like.
So maybe that's what you mean when you say "If you pass no source, it should treat the destination as the source"? But still, you can't then take the destination and obtain a mutable reference from it that'll let you treat it as a source. You really have to write two pieces of logic, one for when source and destination are both given and different, and one where you're operating on the same data.
Maybe with a bit more context on what's behind the scenes of your code (your minimal reproducible example is very terse) we can help more specifically.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With