Implementing Option<> for Custom Type

I would like to implement Option for a custom struct.

pub struct NodeId {
    index: u32,
}

impl NodeId {
    pub fn new() -> NodeId {
        NodeId {
            index: std::u32::MAX
        }
    }

    pub fn is_valid(&self) -> bool {
        self.index != std::u32::MAX
    }
}

The idea is to reserve the value std::u32::MAX as None. This is how I do it in C++, but for Rust I would like to make use of Option<>.

Any ideas?

I don't understand what you mean by "implementing Option for a type". Option is not a trait, it's a (generic) type, "implementing it for a type" doesn't really make sense.

If you want to store a u32 value and indicate that is absent by using the maximal value, you can write a newtype wrapper around u32 and convert it to and from Option<u32>, e.g.

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MaybeU32(u32);

impl MaybeU32 {
    pub fn from_u32(inner: u32) -> Self {
        MaybeU32(inner)
    }

    pub fn to_opt_u32(self) -> Option<u32> {
        if self.0 == u32::MAX {
            None
        } else {
            Some(self.0)
        }
    }
}

But it's hard to tell what exactly you want without some context regarding how you will use this type.

Sorry that my explaination is unclear, i am new to Rust.

What I want to do is to implement a Tree in Rust, and I would like to use indices to address the nodes in the tree.

pub struct Node {
    pub parent: Option<NodeId>,
}

Option would use 8 instead of 4 bytes.

I would like to extend Option so, that it would check if NodeId::index equals std::u32::MAX instead of reserving 4 extra bytes, but I dont know if its possible.

I don't think so, not like that. You could try using 0 as the absent value though, then representing node IDs with Option<NonZero<u32>>, which would also avoid storing a separate discriminant.

3 Likes

You don't get to change Option as it is only dealt with by the compiler.
There is optional crate that might offer what your after.

Option is literally just a regular enum:

pub enum Option<T> {
   None,
   Some(T),
}

It's not a language feature. It's not something you can customize, because it's just a dumb regular type.

There's nothing stopping you from using std::u32::MAX as a placeholder value just based on convention like you would in C++.

An alternative is to use Option combined with std::num::NonZeroU32 and friends as @H2CO3 said. That way you get a type-safe wrapper which will make sure you handle placeholder values, while still having the same size as a normal u32.

use std::mem;
use std::num::NonZeroU32;

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct NodeId(Option<NonZeroU32>);

impl NodeId {
    pub const PLACEHOLDER: NodeId = NodeId(None);

    pub fn new(n: u32) -> Self {
        NodeId(NonZeroU32::new(n))
    }

    pub fn is_valid(&self) -> bool {
        self.0.is_some()
    }
}

impl Default for NodeId {
    fn default() -> NodeId {
        NodeId::PLACEHOLDER
    }
}

fn main() {
    assert_eq!(mem::size_of::<NodeId>(), mem::size_of::<u32>());
}