为了一些琐碎的事情,比如:
fn do_nothing(x: &i64) -> &i64 { x } fn do_nothing(x: &mut i64) -> &mut i64 { x }
你可以让这个泛型用于某个类型T,但是你必须绕过泛型类型,有没有什么方法可以根据传递的参数来推断mut?
mut
gkn4icbw1#
你可以利用你自己的特点:
trait MaybeMutRef {} impl<T: ?Sized> MaybeMutRef for &T {} impl<T: ?Sized> MaybeMutRef for &mut T {} fn do_nothing<T: MaybeMutRef>(x: T) -> T { x }
这个trait本身的值并不是很有用,如果你需要改变基于可变性的行为,你可以通过向trait添加一些成员来实现。
trait MaybeMutRef { type Target: ?Sized; fn get(&self) -> &Self::Target; fn get_mut(&mut self) -> Option<&mut Self::Target>; } impl<T: ?Sized> MaybeMutRef for &T { type Target = T; fn get(&self) -> &Self::Target { self } fn get_mut(&mut self) -> Option<&mut Self::Target> { None } } impl<T: ?Sized> MaybeMutRef for &mut T { type Target = T; fn get(&self) -> &Self::Target { self } fn get_mut(&mut self) -> Option<&mut Self::Target> { Some(self) } } fn do_nothing<T: MaybeMutRef>(mut x: T) { if let Some(x) = x.get_mut() { // Mutable reference, x is a &mut T::Target } else { let x = x.get(); // Immutable reference, x is a &T::Target } }
编译器应该能够内联get*调用,从而为您消除死分支。注意,这是一个有点奇怪的模式,我会小心地在所有地方使用它,但是,如果你有一个算法,除了一两个细节,你可以使参数的可变性为条件之外,对于不可变和可变的情况,逻辑几乎是相同的,那么这种方法可能是有用的。如果引用需要指向特定类型的被引用对象,则可以照常使用通用约束:
get*
fn do_nothing<T: MaybeMutRef<Target=i64>>(mut x: T) { todo!() }
作为最后一点建议,我不认为这是你想在API中公开的函数签名,相反,考虑将trait和函数作为私有细节,并使用更多Rust-y公共函数,如下所示:
fn do_nothing_impl<T: MaybeMutRef<Target=i64>>(mut x: T) { todo!() } pub fn do_nothing(x: &i64) { do_nothing_impl(x); } pub fn do_nothing_mut(x: &mut i64) { do_nothing_impl(x); }
1条答案
按热度按时间gkn4icbw1#
你可以利用你自己的特点:
这个trait本身的值并不是很有用,如果你需要改变基于可变性的行为,你可以通过向trait添加一些成员来实现。
编译器应该能够内联
get*
调用,从而为您消除死分支。注意,这是一个有点奇怪的模式,我会小心地在所有地方使用它,但是,如果你有一个算法,除了一两个细节,你可以使参数的可变性为条件之外,对于不可变和可变的情况,逻辑几乎是相同的,那么这种方法可能是有用的。
如果引用需要指向特定类型的被引用对象,则可以照常使用通用约束:
作为最后一点建议,我不认为这是你想在API中公开的函数签名,相反,考虑将trait和函数作为私有细节,并使用更多Rust-y公共函数,如下所示: