When using yew
, I found a function called neq_assign
which return a bool.
When using ndarray
, a similar trait called AssignElem
exists.
When using rug
, a Assign
trait exists.
What's more, when we write something like let mut a=1;let b=&2;a=b
, an error generated by rustc which force us using a=*b
or a=b.into()
instead since the type of a
and b
is different.
I am surprisingly find that, if rust allow CustomAssign
trait, and bind it to an unused symbol(like ~
(means "similar"), ~~
(means similar, too, could be a little bit different compared to ~
(e.g., allow LHS
uninitialize)), or :=
(which is used in Pascal)) things would become better:
#![feature(min_specialization)]
pub trait CustomAssign<Rhs=Self>{
fn custom_assign(&mut self, rhs:Rhs);
}
impl<T> CustomAssign<T> for T{
#[inline(always)]
default fn custom_assign(&mut self, rhs:T){
*self=rhs
}
}
impl<T:Copy> CustomAssign<&T> for T{
#[inline(always)]
default fn custom_assign(&mut self, rhs:&T){
*self=*rhs
}
}
impl<'a,T:CustomAssign<&'a T>> CustomAssign<&'a mut T> for T{
#[inline(always)]
default fn custom_assign(&mut self, rhs:&'a mut T){
self.custom_assign(rhs as &T)
}
}
fn main(){
let mut a=0;
let mut b=[1,3,5,7,9];
b.iter_mut().for_each(|x|{
// when an `.iter()` is used, the type is borrowed and we have to deref it.
a.custom_assign(a+x);
// a~a+x might be more beautiful.
println!("{}",a)
})
}
If we using things like callling a.custom_assign(b)
by a~b
, we would have an awesome code:
struct BuilderConfig(i64,i64);
struct Builds(i128);
impl Builds{
fn new()->Self{Self(0)}
fn assign_new_config(&mut self,cfg:BuilderConfig){
self.0=((cfg.0 as i128)<<64)+(cfg.1 as i128) // just for example.
}
}
impl CustomAssign<BuilderConfig> for Builds{
fn custom_assign(&mut self,rhs:BuilderConfig){
self.assign_new_config(rhs)
}
}
fn main(){
let a=BuilderConfig(1,-1);
let mut b=Builds::new();
b.custom_assign(a); // would be `b~a`
// actually, if we allow using `:=` to initialize variables, we would have:
// let b:Builds := a;
println!("{}",b.0);
}
IMHO, add :=
and ~
(or ~=
,~~
,~
) will make rust more concise and more beautiful.
If we know a
and b
are in the same type, we have more chance choose a=b
since =
is easy to type
if we know a
and b
are in different type(e.g., rug::Integer
and i32
), using :=
or ~
will allow us code faster.
Here is my questions:
Is there some disadvantage?
Is it worth discussed in internals.rust-lang.org?
Is it worth a pre-RFC?
Some progress: with the idea of @苦瓜小仔 in the Chinese Rust forum, a macro may help achieve the goal, which seems to be a shift-reduce hell..
#[macro_export]
macro_rules! cai {
(@expr_block ($($ex:tt)+) => { $($b1:tt)* } else { $($b2:tt)* } $($tail:tt)*) =>{
$($ex)+ {
cai!($($b1)*);
}else{
cai!($($b2)*);
}
cai!($($tail)*)
};
(@expr_block ($($ex:tt)+) => { $($b1:tt)* } $($tail:tt)*) =>{
$($ex)+ {
cai!($($b1)*);
}
cai!($($tail)*)
};
({ $($b1:tt)* } $($tail:tt)*) =>{
{
cai!($($b1)*);
};
cai!($($tail)*)
};
(@split_exp_block ($($ex:tt)+) => { $($b:tt)* } $($tail:tt)*) => {
cai!(@expr_block ($($ex)+) => {$($b)*} $($tail)*)
};
(@split_exp_block ($($ex:tt)+) => $t:tt $($tail:tt)*) => {
cai!(@split_exp_block ($($ex)+ $t) => $($tail)*)
};
(if $t:tt $($tail:tt)*) => {
cai!(@split_exp_block (if $t) => $($tail)*)
};
(while $t:tt $($tail:tt)*) => {
cai!(@split_exp_block (while $t) => $($tail)*)
};
(break { $($b:tt)* } $($tail:tt)*) => {
break { cai!($($b)*); };
cai!($($tail)*)
};
(loop { $($b:tt)* } $($tail:tt)*) => {
loop { cai!($($b)*); } ;
cai!($($tail)*)
};
(for $t:tt $($tail:tt)*) => {
cai!(@split_exp_block (for $t) => $($tail)*)
};
({ $($b:tt)* } $($tail:tt)*) => {
{ cai!($($b:tt)*) }
cai!($($tail)*)
};
($id:ident ~ $ex:expr; $($tail:tt)*) => {
$id.custom_assign(cai!($ex));
cai!($($tail)*)
};
($($id:ident)+ $(: $type:ty)? : = $ex:expr; $($tail:tt)*) => {
$($id)+ $(: $type)? = CustomInitialize::custom_initialize(cai!($ex));
cai!($($tail)*)
};
($st:stmt; $($tail:tt)*) => {
$st
cai!($($tail)*)
};
($ex:expr) => {
$ex
};
() => {};
}