Skip to content

Add a match_class! macroΒ #771

@OddCoincidence

Description

@OddCoincidence

Summary

Add a match_class! macro for implementing behavior that varies based on the built-in type of an object.

Detailed Explanation

Doing this currently with downcast is a bit awkward. Because it consumes the Rc,
you have two options:

  1. Clone the object and then do a series of ifs.
impl PyFloat {
    fn eq(self: PyRef<Self>, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
        if let Some(i @ PyInt { .. }) = other.clone().downcast() {
            Ok(PyInt::new(&self.value + i.as_bigint()).into_ref(vm))
        } else if let Some(f @ PyFloat { .. }) = other.downcast() {{
            let output = self.value + f.value.to_bigint().unwrap();
            Ok(PyFloat::new(output))
        } else {
            Ok(vm.ctx.not_implemented())
        }
    }
}
  1. Use the returned Err(obj) of downcast to avoid the clone.
match other.downcast() {
    Ok(i @ PyInt { ..}) => {
        Ok(PyInt::new(&self.value + ).into_ref(vm))
    },
    Err(obj) => match obj.downcast() {
        Ok(f @ PyFloat { .. }) => {
            let output = self.value + f.value.to_bigint().unwrap();
            Ok(PyFloat::new(output))
        },
        _ => Ok(vm.ctx.not_implemented()),
    }
}

This avoids a pointless clone at the cost of adding undesirable rightward drift. What we really want is to be able to use a match-like syntax that expands to the efficient version above:

impl PyFloat {
    fn eq(self: PyRef<Self>, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
        match_object!(other,
            i @ PyInt { .. } => {
                Ok(PyInt::new(&self.value + i.as_bigint()).into_ref(vm))
            },
            f @ PyFloat { .. } => {
                let output = self.value + f.value.to_bigint().unwrap();
                Ok(PyFloat::new(output))
            },
            _ => Ok(vm.ctx.not_implemented()),
        )
    }
}

Drawbacks, Rationale, and Alternatives

The main drawback is that it is another macro. It should be very straightforward to implement and to understand though.

Unresolved Questions

Will allowing a full pattern in each match clause encourage making struct fields public, when they really should not be? An alternative would be to just allow the type name in each match arm.

Metadata

Metadata

Assignees

No one assigned

    Labels

    RFCRequest for comments

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions