有没有一种方法可以“超载”使用相同的方法,但具有不同的参数?

发布于 2025-01-28 07:15:57 字数 3508 浏览 1 评论 0原文

这是美好的一天,直到我看到Python的TKINTER能够获取/设置数据的能力,如下所示:

k = some_object(inner=7)
print(k.inner()) # Gets the value, prints 7
k.inner(4) # Sets the value
print(k.inner()) # 4

我印象深刻 - 非常酷!无需编写所有这些讨厌的set_dataget_data,只是一种决定本身可以做什么的方法!

但是后来我在Rust尝试了同样的事情。 首先,内部不能成为一个函数,因为Rust的功能不支持过载。通常通过手动实现fnoncefnmutfn在某些单元类型上,然后声明该单元的常数(请参见 Overloadf板条箱)。尽管它需要一些每晚的功能,但我目前正在使用夜间生锈,因此这不是问题。

但是的问题是,方法是将self作为参数(这就是为什么它是方法而不是函数),并且您不能使用点表示法:

#![feature(fn_traits, unboxed_closures)]

/// Some `sort` of a function
pub struct Functor;

impl FnOnce for Functor { ... }

impl FnMut for Functor { ... }

impl Fn for Functor { ... }

#[allow(#[allow(non_upper_case_globals)])]
pub const i_am_a_normal_function_trust_me: Functor = Functor;

// ...

i_am_a_normal_function_trust_me() // Yay! Could be called!

pub struct Wrapper(());

impl Wrapper {
    /// Let's just say that `Functor` takes a `&Wrapper`
    #[allow(non_upper_case_globals)]
    pub const method: Functor = Functor;
}

let v = Wrapper(());

// Oopsy, does not work
v.method()

// Only through that ugly notation(which is not what I want)
Wrapper::method(&v)

这不会编译,因此我尝试了另一种方法 - 通过普通特征:

pub trait GetInner <T> {
    fn inner(&self) -> &T;
}

pub trait SetInner <T> {
    fn inner(&mut self, inner: T);
}

pub struct Wrapper <T> (T);

impl <T> GetInner <T> for Wrapper <T> {
    fn inner(&self) -> &T {
        &self.0
    }
}

impl <T> SetInner <T> for Wrapper <T> {
    fn inner(&mut self, inner: T) {
        self.0 = inner
    }
}

fn main() {
    let mut v = Wrapper(0);
    dbg!(v.inner());
    v.inner(4); // error[E0061]: this function takes 0 arguments but 1 argument was supplied
    dbg!(v.inner());
    v.inner(v.inner() + 2); // error[E0061]: this function takes 0 arguments but 1 argument was supplied
    dbg!(v.inner());
}

因此,编译器无法区分单独特征的方法,即使它们的签名完全不同!

好的,尝试,不错,尝试...但是 polska 这个想法尚未丢失!

这次,我注意到编译器仅抱怨setInner并尝试交换含义:


pub trait GetInner <T> {
    fn inner(&mut self) -> &T;
}

pub trait SetInner <T> {
    fn inner(&mut self, inner: T);
}

pub struct Wrapper <T> (T);

/// Swappy swap
impl <T> SetInner <T> for Wrapper <T> {
    fn inner(&mut self, inner: T) {
        self.0 = inner
    }
}

/// Swappy swap
impl <T> GetInner <T> for Wrapper <T> {
    fn inner(&mut self) -> &T {
        &self.0
    }
}

fn main() {
    let mut v = Wrapper(0);
    dbg!(v.inner()); // error[E0034]: multiple applicable items in scope
    v.inner(4); // error[E0034]: multiple applicable items in scope
    dbg!(v.inner()); // error[E0034]: multiple applicable items in scope
    v.inner(v.inner() + 2); // error[E0034]: multiple applicable items in scope x 2
    dbg!(v.inner()); // error[E0034]: multiple applicable items in scope
}

怪异,错误现在完全不同。但这对我没有帮助...

我从上方寻求一个明亮的指导:有什么方法可以将我的愿望实现到现实,还是目前的生锈(甚至每晚)还没有为这种“先进技术”做好准备?

It was a beautiful day until I saw python's Tkinter ability to get/set data on a value, like follows:

k = some_object(inner=7)
print(k.inner()) # Gets the value, prints 7
k.inner(4) # Sets the value
print(k.inner()) # 4

I was impressed - that is very cool! No need to write all these pesky set_data or get_data, just a single method that decides what to do all by itself!

But then I tried the same thing in Rust.
Firstly, that inner can't be a function, since Rust's functions do not support overloading. This problem is often solved by manually implementing FnOnce, FnMut and Fn on some unit type and then declaring a constant of that unit(see overloadf crate). Although it requires some nightly features, I am currently using nightly Rust so it's not a problem.

But what is a problem is that method takes self as an argument(that's why it's method and not a function), and you cannot 'call' a constant using dot notation:

#![feature(fn_traits, unboxed_closures)]

/// Some `sort` of a function
pub struct Functor;

impl FnOnce for Functor { ... }

impl FnMut for Functor { ... }

impl Fn for Functor { ... }

#[allow(#[allow(non_upper_case_globals)])]
pub const i_am_a_normal_function_trust_me: Functor = Functor;

// ...

i_am_a_normal_function_trust_me() // Yay! Could be called!

pub struct Wrapper(());

impl Wrapper {
    /// Let's just say that `Functor` takes a `&Wrapper`
    #[allow(non_upper_case_globals)]
    pub const method: Functor = Functor;
}

let v = Wrapper(());

// Oopsy, does not work
v.method()

// Only through that ugly notation(which is not what I want)
Wrapper::method(&v)

This does not compile, so I tried an another approach - through ordinary traits:

pub trait GetInner <T> {
    fn inner(&self) -> &T;
}

pub trait SetInner <T> {
    fn inner(&mut self, inner: T);
}

pub struct Wrapper <T> (T);

impl <T> GetInner <T> for Wrapper <T> {
    fn inner(&self) -> &T {
        &self.0
    }
}

impl <T> SetInner <T> for Wrapper <T> {
    fn inner(&mut self, inner: T) {
        self.0 = inner
    }
}

fn main() {
    let mut v = Wrapper(0);
    dbg!(v.inner());
    v.inner(4); // error[E0061]: this function takes 0 arguments but 1 argument was supplied
    dbg!(v.inner());
    v.inner(v.inner() + 2); // error[E0061]: this function takes 0 arguments but 1 argument was supplied
    dbg!(v.inner());
}

So... Compiler couldn't distinguish between methods of separate traits even though their signature is completely different!

Okay, nice try, nice try... But Polska the idea is not yet lost!

This time I noticed that compiler complains only about SetInner and tried swapping impls:


pub trait GetInner <T> {
    fn inner(&mut self) -> &T;
}

pub trait SetInner <T> {
    fn inner(&mut self, inner: T);
}

pub struct Wrapper <T> (T);

/// Swappy swap
impl <T> SetInner <T> for Wrapper <T> {
    fn inner(&mut self, inner: T) {
        self.0 = inner
    }
}

/// Swappy swap
impl <T> GetInner <T> for Wrapper <T> {
    fn inner(&mut self) -> &T {
        &self.0
    }
}

fn main() {
    let mut v = Wrapper(0);
    dbg!(v.inner()); // error[E0034]: multiple applicable items in scope
    v.inner(4); // error[E0034]: multiple applicable items in scope
    dbg!(v.inner()); // error[E0034]: multiple applicable items in scope
    v.inner(v.inner() + 2); // error[E0034]: multiple applicable items in scope x 2
    dbg!(v.inner()); // error[E0034]: multiple applicable items in scope
}

Weird, errors are completely different now. But this doesn't help me...

I ask for a bright guidance from above: is there any way to implement my wish into reality, or current Rust(even nightly) is not yet prepared for such an 'advanced technology'?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

旧人哭 2025-02-04 07:15:57

不。不要在生锈中实施过载。甚至不要尝试。这不是一个很好的Rust API设计。生锈不使用过载。生锈不应使用过载。使用特质。


当然,但是,我们可以吗?

特征方法不适用于 + 特征,但它将与a 单一通用性特征(尽管呼叫不便利):

trait Overload<Args> {
    type Output;
    fn foo(&mut self, args: Args) -> Self::Output;
}

struct Data(i32);

impl Overload<()> for Data {
    type Output = i32;
    fn foo(&mut self, (): ()) -> i32 { self.0 }
}

impl Overload<(i32,)> for Data {
    type Output = ();
    fn foo(&mut self, (v,): (i32,)) { self.0 = v; }
}

fn main() {
    let mut v = Data(0);
    v.foo((123,));
    dbg!(v.foo(()));
}

我还没有(?)找到一种使fn*方法可悲的方法。

Don't. Don't implement overloading in Rust. Don't even try. This is not a good Rust API design. Rust does not use overloading. Rust should not use overloading. Use traits.


Sure, but still, can we?

The trait approach does not work with multiple traits, but it will work with a single generic trait (though the call is... not convenient):

trait Overload<Args> {
    type Output;
    fn foo(&mut self, args: Args) -> Self::Output;
}

struct Data(i32);

impl Overload<()> for Data {
    type Output = i32;
    fn foo(&mut self, (): ()) -> i32 { self.0 }
}

impl Overload<(i32,)> for Data {
    type Output = ();
    fn foo(&mut self, (v,): (i32,)) { self.0 = v; }
}

fn main() {
    let mut v = Data(0);
    v.foo((123,));
    dbg!(v.foo(()));
}

Playground.

I haven't (yet?) found a way to make the Fn* approach works, sadly.

-残月青衣踏尘吟 2025-02-04 07:15:57

模仿 Rustier 方式的TKINTER能力:
如op所述 - 允许 fn innit() self self 参数可能不是相互的。

use std::cell::RefCell;

struct SomeObject<T> {
    inner: RefCell<T>,
}

impl SomeObject<i32> {
    fn new() -> SomeObject<i32> {
        SomeObject { inner: RefCell::new(0), }
    }

    fn inner(&self, opt: Option<i32>) -> i32 {
        match opt {
            Some(n) => { *self.inner.borrow_mut() = n; n },
            None    => { *self.inner.borrow() },
        }
    }
}

fn main() {
    let some_object = SomeObject::new();
    some_object.inner(Some(123));
    println!("{}", some_object.inner(Some(some_object.inner(None) + 2)));  // 125
}

mimicking the Tkinter ability in a rustier way:
As mentioned by the OP – to allow the cascaded call of fn inner() the self parameter may not be mutual.

use std::cell::RefCell;

struct SomeObject<T> {
    inner: RefCell<T>,
}

impl SomeObject<i32> {
    fn new() -> SomeObject<i32> {
        SomeObject { inner: RefCell::new(0), }
    }

    fn inner(&self, opt: Option<i32>) -> i32 {
        match opt {
            Some(n) => { *self.inner.borrow_mut() = n; n },
            None    => { *self.inner.borrow() },
        }
    }
}

fn main() {
    let some_object = SomeObject::new();
    some_object.inner(Some(123));
    println!("{}", some_object.inner(Some(some_object.inner(None) + 2)));  // 125
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文