< 返回版块

Neutron3529 发表于 2021-12-25 15:38

我在users.rust-lang.org开了一个帖子,询问了相关问题

反对意见居多,但大概是我步子迈太大的缘故

——我的提议允许大家使用x~uniform;来给x赋一个新的,产生自随机数生成器uniform的随机数值,原理如下

impl CustomAssign<RNG> for i32{
    fn custom_assign(&mut self,rhs:&mut RNG){
        *self=rhs.sample();
    }
}
fn main(){
    let uniform=RNG::uniform(0,1);// RNG 是什么我就不写了
    let mut x:f64=0;//可变借用x的前提是x已经完成初始化
    x~uniform;//其实是x.custom_assign(&mut uniform);
}

我有这个想法,最初的原因只是发现,Rust有各种assign的重载,但不能重载=,也没有重载assign的任何方法。

我们的确可以重载|= ^= &=之类的算符来完成自定义assign的功能,但考虑到这几个符号都有自己独特的语义,借助这几个重载符号容易引起歧义

同时,类似a=From::from(b)或者a=b.into()的赋值有点过于笨拙

于是我想干脆一步到位,定义两个可以customize的trait,并绑定不同的符号

一个是CustomAssign,绑定~,左边是可变借用,右边是T,T可以直接传递所有权,或者借用,或者可变借用。

另一个是CustomInitialize,绑定:=,trait只有对右边的借用,签名如下:

trait CustomInitialize<Rhs=Self>{//默认Rhs跟Self相同,但Rhs可以随便改
    fn custom_initialize(rhs:Rhs)->Self;//Self会赋值给左边
}

这里“绑定”的意思是

//预先定义
impl CustomInitialize<i32> for i128{
    fn custom_initialize(rhs:i32)->Self{rhs as i128}
}
// ...

let x:i128 :=1i32;
//等价于下面的式子
let x:i128 = <i128 as CustomInitialize<i32>>::custom_initialize(1i32);
//写完才发现,我在这里竟要求了一个冒号等价于这么多东西

这两种定义会不会存在什么潜在的问题?

我又应该以什么样的方法把类似feature request提交给rust论坛呢?

(只是担心,在users里面发文章都被喷,发到internal那里……)

评论区

写评论
作者 Neutron3529 2021-12-26 20:53

我不想用

cai!(a~b);
cai!(c:=d);
e=cai!(a~b); // 暂时没想好该不该允许这玩意有返回值

这么啰嗦的指令,毕竟

a.a(b);
c.i(d);
e=a.a(b); // 暂时没想好该不该允许这玩意有返回值

比上面的代码更短且更好敲


讲道理我还是写了两个syntax的

一个是a~b,调用a.custom_assign(b)

一个是a:=b,调用a=CustomInitialize::custom_initialize(b)

这两个应该算是新语法了


至于“大多数宏都尽量把解析内容尽可能缩小”

我读的是这个,成品是

fn main() {
    let _ = Ook!(
        Ook. Ook?  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook! Ook?  Ook? Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook?  Ook! Ook!  Ook? Ook!  Ook? Ook.
        Ook! Ook.  Ook. Ook?  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook! Ook?  Ook? Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook?
        Ook! Ook!  Ook? Ook!  Ook? Ook.  Ook. Ook.
        Ook! Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook! Ook.  Ook! Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook! Ook.  Ook. Ook?  Ook. Ook?
        Ook. Ook?  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook! Ook?  Ook? Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook?
        Ook! Ook!  Ook? Ook!  Ook? Ook.  Ook! Ook.
        Ook. Ook?  Ook. Ook?  Ook. Ook?  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook! Ook?  Ook? Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook?  Ook! Ook!  Ook? Ook!  Ook? Ook.
        Ook! Ook!  Ook! Ook!  Ook! Ook!  Ook! Ook.
        Ook? Ook.  Ook? Ook.  Ook? Ook.  Ook? Ook.
        Ook! Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook! Ook.  Ook! Ook!  Ook! Ook!  Ook! Ook!
        Ook! Ook!  Ook! Ook!  Ook! Ook!  Ook! Ook.
        Ook! Ook!  Ook! Ook!  Ook! Ook!  Ook! Ook!
        Ook! Ook!  Ook! Ook!  Ook! Ook!  Ook! Ook!
        Ook! Ook.  Ook. Ook?  Ook. Ook?  Ook. Ook.
        Ook! Ook.  Ook! Ook?  Ook! Ook!  Ook? Ook!
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook. Ook.  Ook. Ook.
        Ook. Ook.  Ook. Ook.  Ook! Ook.
    );
}

写成这样已经很不容易了……

(BTW,下一步的计划是上proc_marco,加速编译,已经找到了dalao的栗子,正在仔细研读)

--
👇
苦瓜小仔: 似乎你想把所有 Rust 语法放到一个宏里面,但这根本不必要。宏的主要作用(via Rust By Example):

  1. 减少重复
  2. 创造语法
  3. 变长参数

大多数宏都尽量把解析内容尽可能缩小,从而尽可能不对编译(时间、优化等方面)很大造成影响,而你反其道而行之。

你把 block 的部分纳入进来,既没有减少重复代码(该写的 Rust 语法还是得写),你也没有创造新的语法(该写的语法还是 Rust 语法),更无关变长参数。

我不懂,但我大受震撼:)

苦瓜小仔 2021-12-26 18:21

似乎你想把所有 Rust 语法放到一个宏里面,但这根本不必要。宏的主要作用(via Rust By Example):

  1. 减少重复
  2. 创造语法
  3. 变长参数

大多数宏都尽量把解析内容尽可能缩小,从而尽可能不对编译(时间、优化等方面)很大造成影响,而你反其道而行之。

你把 block 的部分纳入进来,既没有减少重复代码(该写的 Rust 语法还是得写),你也没有创造新的语法(该写的语法还是 Rust 语法),更无关变长参数。

我不懂,但我大受震撼:)

作者 Neutron3529 2021-12-26 01:44

如果回到大四作死选编译原理的那段时光

我觉得我能拿满分……

最后搞出来的macro整个就是一shift-reduce现场:

macro_rules! cai {
    (loop $b:block $($tail:tt)*) => {
        loop { cai!($b) }
        cai!($($tail)*)
    };
    (@while-expr-block $ex:expr ; $b:block $($tail:tt)*) => {
        while $ex $b
        cai!($($tail)*)
    };
    (@expr_block $i:ident ($ex:expr) =>  { $($b1:tt)* } else  { $($b2:tt)* } $($tail:tt)*) =>{
        $i $ex {
            cai!($($b1)*);
        }else{
            cai!($($b2)*);
        }
        cai!($($tail)*)
    };
    (@expr_block $i:ident ($($ex:tt)*) => { $($b1:tt)* } $($tail:tt)*) =>{
        $i $($ex)* {
            cai!($($b1)*);
        }
        cai!($($tail)*)
    };
    (@split_exp_block $i:ident ($($ex:tt)*) => { $($b:tt)* } $($tail:tt)*) => {
        cai!(@expr_block $i ($($ex)*) => {$($b)*} $($tail)*)
    };
    (@split_exp_block $i:ident ($($ex:tt)*) => $t:tt $($tail:tt)*) => {
        cai!(@split_exp_block $i ($($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)*)
    };
    (for $t:tt $($tail:tt)*) => {
        cai!(@split_exp_block for ($t) => $($tail)*)
    };
    ($id:ident ~ $ex:expr; $($tail:tt)*) => {
        $id.custom_assign($ex);
        cai!($($tail)*)
    };
    ($($id:ident)+ $(: $type:ty)? : = $ex:expr; $($tail:tt)*) => {
        $($id)+ $(: $type)? = CustomInitialize::custom_initialize($ex);
        cai!($($tail)*)
    };
    ($st:stmt; $($tail:tt)*) => {
        $st
        cai!($($tail)*)
    };
    ($ex:expr) => {
        $ex
    };
    () => {};
}
作者 Neutron3529 2021-12-25 23:35

现在大概还需要处理一下block的问题

总觉得已经不是什么问题了

大不了if a==b {cai_exec!{}}解决……

正在研读https://danielkeep.github.io/tlborm/book/

之前明明感觉自己挺擅长Rust的

现在发现……我不会的还很多……

--
👇
苦瓜小仔: 其实还可以简化:

// ... all implementation remained here

macro_rules! cai_exec {
    ($id:ident ~ $ex:expr; $($tail:tt)*) => {
        $id.custom_assign($ex);
        cai_exec!($($tail)*);
    };
    ($($id:ident)+ $(: $type:ty)? : = $ex:expr; $($tail:tt)*) => {
        $($id)+ $(: $type)? = CustomInitialize::custom_initialize($ex);
        cai_exec!($($tail)*);
    };
    ($st:stmt; $($tail:tt)*) => {
        $st
        cai_exec!($($tail)*);
    };
    ($ex:expr) => {
        $ex
    };
    () => {};
}

fn main() {
    cai_exec! {let _a:i32 :=1;};
    cai_exec! {{let _a:i32 =1;}};
    cai_exec! {let mut a:i32 :=1; a~1;};
    cai_exec! {let mut a:i32 :=1; a~1; let c @ b = a; dbg!(b,c)}; // 模式一侧可能是任意多的 ident
    cai_exec! {
        let _a=1;            //stmt
        let mut a:i32 :=1;  //:=初始化语句,需要手工翻译
        a~1;                //赋值语句,需要手工翻译
        a+=1;               // stmt
        a.custom_assign(1)  // expr,有可能不以分号结尾
    };
}
苦瓜小仔 2021-12-25 22:45

其实还可以简化:

// ... all implementation remained here

macro_rules! cai_exec {
    ($id:ident ~ $ex:expr; $($tail:tt)*) => {
        $id.custom_assign($ex);
        cai_exec!($($tail)*);
    };
    ($($id:ident)+ $(: $type:ty)? : = $ex:expr; $($tail:tt)*) => {
        $($id)+ $(: $type)? = CustomInitialize::custom_initialize($ex);
        cai_exec!($($tail)*);
    };
    ($st:stmt; $($tail:tt)*) => {
        $st
        cai_exec!($($tail)*);
    };
    ($ex:expr) => {
        $ex
    };
    () => {};
}

fn main() {
    cai_exec! {let _a:i32 :=1;};
    cai_exec! {{let _a:i32 =1;}};
    cai_exec! {let mut a:i32 :=1; a~1;};
    cai_exec! {let mut a:i32 :=1; a~1; let c @ b = a; dbg!(b,c)}; // 模式一侧可能是任意多的 ident
    cai_exec! {
        let _a=1;            //stmt
        let mut a:i32 :=1;  //:=初始化语句,需要手工翻译
        a~1;                //赋值语句,需要手工翻译
        a+=1;               // stmt
        a.custom_assign(1)  // expr,有可能不以分号结尾
    };
}
作者 Neutron3529 2021-12-25 22:38

好神奇

果然我不适合写marco

这么做应该相当完美了

作死试了试,只有类似下面这样的代码才不能通过编译(必须在里面多写一个cai_exec!

macro_rules! another{
    ()=>{let a:i32 :=2i32;}
};

相关代码会被我整理之后送进https://github.com/Neutron3529/obfuscate-integercrates.io

协议是GPL-v3 or later

如果这么搞,我大概可以先自娱自乐一下,等想法成熟之后,再把“重载赋值函数”这个功能当RFC提出来

感谢你的帮助:)

--
👇
苦瓜小仔: 你可以试试这个:

macro_rules! cai_exec {
    ($id:ident ~ $ex:expr; $($tail:tt)*) => {
        $id.custom_assign($ex);
        cai_exec!($($tail)*);
    };
    ($id:ident : = $ex:expr; $($tail:tt)*) => {
        $id = CustomInitialize::custom_initialize($ex);
        cai_exec!($($tail)*);
    };
    (let $id:ident $(: $type:ty)? : = $ex:expr; $($tail:tt)*) => {
        let $id $(: $type)? = CustomInitialize::custom_initialize($ex);
        cai_exec!($($tail)*);
    };
    (let mut $id:ident $(: $type:ty)? : = $ex:expr; $($tail:tt)*) => {
        let mut $id $(: $type)? = CustomInitialize::custom_initialize($ex);
        cai_exec!($($tail)*);
    };
    ($st:stmt; $($tail:tt)*) => {
        $st
        cai_exec!($($tail)*);
    };
    ($ex:expr) => {
        $ex
    };
}
苦瓜小仔 2021-12-25 22:22

你可以试试这个:

macro_rules! cai_exec {
    ($id:ident ~ $ex:expr; $($tail:tt)*) => {
        $id.custom_assign($ex);
        cai_exec!($($tail)*);
    };
    ($id:ident : = $ex:expr; $($tail:tt)*) => {
        $id = CustomInitialize::custom_initialize($ex);
        cai_exec!($($tail)*);
    };
    (let $id:ident $(: $type:ty)? : = $ex:expr; $($tail:tt)*) => {
        let $id $(: $type)? = CustomInitialize::custom_initialize($ex);
        cai_exec!($($tail)*);
    };
    (let mut $id:ident $(: $type:ty)? : = $ex:expr; $($tail:tt)*) => {
        let mut $id $(: $type)? = CustomInitialize::custom_initialize($ex);
        cai_exec!($($tail)*);
    };
    ($st:stmt; $($tail:tt)*) => {
        $st
        cai_exec!($($tail)*);
    };
    ($ex:expr) => {
        $ex
    };
    () => {};
}
作者 Neutron3529 2021-12-25 20:57

我想做一个可以正常翻译如下程序的marco

fn main(){
cai!{
    let a=1;            //stmt
    let mut a:i32 :=1;  //:=初始化语句,需要手工翻译
    a~1;                //赋值语句,需要手工翻译
    a+=1;               // stmt
    a.custom_assign(1)  // expr,有可能不以分号结尾
}
}

目前的成果是,发现了$t:tt这个比较好用的匹配工具,但,不知为什么匹配的时候总会出问题

macro_rules! cai {
    ($id:ident ~ $ex:expr;$($t:tt)*) => {
        $id.custom_assign($ex);
        cai!{$($t)*}
    };
    ($id:ident : = $ex:expr;$($t:tt)*) => {
        $id = CustomInitialize::custom_initialize($ex);
        cai!{$($t)*}
    };
    (let $id:ident $(: $type:ty)? : = $ex:expr ; $($t:tt)*) => {
        let $id $(: $type)? = CustomInitialize::custom_initialize($ex);
        cai!{$($t)*}
    };
    (let mut $id:ident $(: $type:ty)? : = $ex:expr ; $($t:tt)*) => {
        let mut $id $(: $type)? = CustomInitialize::custom_initialize($ex);
        cai!{$($t)*}
    };
    ($($st:stmt;)+ $($t:tt)+) => {$($st);+cai!{$($t)+}};
    ($($ex:expr;)+ $($t:tt)+) => {$($ex);+cai!{$($t)+}};
    ($ex:expr) => {$ex};
    ($st:stmt) => {$st};
    ()=>{};
}

比如程序会觉得let像一个stmt,从而报错说某个带:=赋值的语句有歧义

👇
苦瓜小仔: 是的:每条语法规则是你自己定义的,所以要解析语法。但你可以用其他技巧简化,比如 $( ... )? 表示零次或一次。

macro_rules! cai_exec {
    ($id:ident ~ $ex:expr) => {
        $id.custom_assign($ex);
    };
    ($id:ident : = $ex:expr) => {
        $id = CustomInitialize::custom_initialize($ex);
    };
    (let $id:ident $(: $type:ty)? : = $ex:expr) => {
        let $id $(: $type)? = CustomInitialize::custom_initialize($ex);
    };
}

即使你用(函数式)过程宏,你也得自己解析语法。

你使用 cai_exec 不是解决了 ~:= 的问题吗?我不明白你的第二个问题 —— “一次marco解决所有问题”。

苦瓜小仔 2021-12-25 19:15

是的:每条语法规则是你自己定义的,所以要解析语法。但你可以用其他技巧简化,比如 $( ... )? 表示零次或一次。

macro_rules! cai_exec {
    ($id:ident ~ $ex:expr) => {
        $id.custom_assign($ex);
    };
    ($id:ident : = $ex:expr) => {
        $id = CustomInitialize::custom_initialize($ex);
    };
    (let $id:ident $(: $type:ty)? : = $ex:expr) => {
        let $id $(: $type)? = CustomInitialize::custom_initialize($ex);
    };
}

即使你用(函数式)过程宏,你也得自己解析语法。

你使用 cai_exec 不是解决了 ~:= 的问题吗?我不明白你的第二个问题 —— “一次marco解决所有问题”。

作者 Neutron3529 2021-12-25 17:50

完整marco(可以编译,但只能逐句加marco)的定义在这里

话说,想实现“一次marco解决所有问题”这个目标不会要用到#[proc_marco]吧……

#![feature(min_specialization)]
pub trait CustomAssign<Rhs=Self>{ // bind to a~b
    fn custom_assign(&mut self, rhs:Rhs); // a~b means a.custom_assign(b)
}
pub trait CustomInitialize<Rhs=Self>{ // bind to a:=b
    fn custom_initialize(rhs:Rhs)->Self; // a:=b means a=<type of a>::custom_initialize(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)
    }
}
impl<T> CustomInitialize<T> for T{
    #[inline(always)]
    default fn custom_initialize(rhs:T)->Self{
        rhs
    }
}
impl<T:Copy> CustomInitialize<&T> for T{
    #[inline(always)]
    default fn custom_initialize(rhs:&T)->Self{
        *rhs
    }
}
impl<'a,T:CustomInitialize<&'a T>> CustomInitialize<&'a mut T> for T{
    #[inline(always)]
    default fn custom_initialize(rhs:&'a mut T)->Self{
        Self::custom_initialize(rhs as &T)
    }
}


impl CustomAssign<i64> for i32{
    fn custom_assign(&mut self,rhs:i64){
        *self=rhs as i32;
    }
}
impl CustomInitialize<i64> for i32{
    fn custom_initialize(rhs:i64)->Self{
        rhs as i32
    }
}

macro_rules! cai_exec {
    ($id:ident ~ $ex:expr)=>{$id.custom_assign($ex);};
    ($id:ident := $ex:expr)=>{$id=CustomInitialize::custom_initialize($ex);};
    (let $id:ident := $ex:expr)=>{let $id=CustomInitialize::custom_initialize($ex);};
    (let $id:ident : $type:ty := $ex:expr)=>{let $id:$type=CustomInitialize::custom_initialize($ex);};
}

fn main(){
    let mut a=2i32;
    cai_exec!{a ~ 1i64}
    cai_exec!{let b:i32 := 2i64}
    cai_exec!{a := 3i64}
    println!("{}",a+b)
}
作者 Neutron3529 2021-12-25 17:20

感谢回复

你说的“宏”的确是一个好主意

我总觉得可以直接用宏完成我想做的事情(毕竟我只需要一个语法糖)

目前已经可以用宏把:=~绑定到对应的trait

但这么做稍显繁琐

macro_rules! cai { // Custom Assign and Initialize的缩写
    ($id:ident ~ $ex:expr)=>{$id.custom_assign($ex);};
}
fn main(){
    let mut a=1;
    cai!{a ~ 1i64}
    println!("{}",a)
}

有没有办法把宏变成这样呢:

macro_rules! cai {
    //?
}
fn main(){
    cai!{
        let mut a=1;
        a ~ 1i64
        println!("{}",a)
    }
}

--
👇
苦瓜小仔: 我觉得原贴下的评论都在理啊,没有觉得你被“喷”。你只是听到合理的反对的声音而已。

你的在这里的帖子里的问题太多了,而且针对最核心的问题,你的确没有展示出强有力的理由说服别人接受或者使用你所定义的新符号。

正如 @H2CO3 所言:

To be perfectly honest, I highly doubt that.

First of all, writing speed is not the bottleneck in getting things done.

Second, it just doesn't matter all that much.

Code is read much more than it is written, and a language should optimize for the understandability of the code, and not on making it easy to hammer out undecipherable blobs of symbols as "fast" as possible.

从我自己的角度看,如果只是为了方便使用,完全可以试试宏,它的功能之一就是提供 DST —— 创造特定领域的语法,比如 r!(x~uniform)

此外,语法简洁并不是编程核心考量的问题 —— 至少不是 Rust (及其社区)所主要考量的问题。

苦瓜小仔 2021-12-25 16:43

我觉得原贴下的评论都在理啊,没有觉得你被“喷”。你只是听到合理的反对的声音而已。

你的在这里的帖子里的问题太多了,而且针对最核心的问题,你的确没有展示出强有力的理由说服别人接受或者使用你所定义的新符号。

正如 @H2CO3 所言:

To be perfectly honest, I highly doubt that.

First of all, writing speed is not the bottleneck in getting things done.

Second, it just doesn't matter all that much.

Code is read much more than it is written, and a language should optimize for the understandability of the code, and not on making it easy to hammer out undecipherable blobs of symbols as "fast" as possible.

从我自己的角度看,如果只是为了方便使用,完全可以试试宏,它的功能之一就是提供 DST —— 创造特定领域的语法,比如 r!(x~uniform)

此外,语法简洁并不是编程核心考量的问题 —— 至少不是 Rust (及其社区)所主要考量的问题。

1 共 12 条评论, 1 页