Do we have member references?

In C++ there is a member pointer which is actually an offset from the beginning of the structure (along with type of course).
To dereference it, you must provide pointer to struct.
This is much like we store offset for the string.

Do we have similar thing in Rust?

Here is an C++ code to illustrate my idea.

#include <iostream>

namespace
{
	struct user
	{
		std::string m_name;
		std::string m_last_name;
		std::string user::* m_goes_first;

		user(std::string name, std::string last_name, const bool name_goes_first) :
			m_name(std::move(name)),
			m_last_name(std::move((last_name))),
			m_goes_first(name_goes_first ? &user::m_name : &user::m_last_name)
		{
		}

		const std::string& first() const
		{
			return this->*(this->m_goes_first);
		}
	};
}

int main()
{
	const user english{"John", "Johnson", true}, hungarian{"Charles", "Simonyi", false};
	std::cout << english.first() << " and " << hungarian.first() << std::ends;
}

How can I implement first() method in rust?

1 Like

Most people would just use a closure if they need a way to dynamically access one field or another based between a condition.

struct User {
  name: String,
  last_name: String,
}

fn main() {
  let name_goes_first = false;
  let first = |u: &User| if name_goes_first { &u.name } else { &u.last_name };
  ...  
}

Otherwise you could just store the condition in the User struct.

struct User {
  name: String,
  last_name: String,
  name_goes_first: bool,
}

Member references are one of those obscure C++ features that you only really need to reach for in tricky situations, and I don't believe I've seen anything similar in any other language. Technically, it's quite possible to implement the equivalent in Rust as a library because there are ways to calculate offsets of a member and wrap all the unsafety up in a nice API, but I feel like it's got limited value.

2 Likes

there's no member references similar to how C++ does it, but you can emulate similar feature in various ways.

one example is to use a function pointer (possibly with some macros to simplify the callsite a bit):

struct User {
    name: String,
    last_name: String,
    goes_first: Projector,
}
type Projector = fn(user: &User) -> &str;
impl User {
    fn first(&self) -> &str {
        (self.goes_first)(self)
    }
}
macro_rules! project_member {
    ($member: ident) => {
        (|user: &User| &user.$member) as Projector
    }
}

fn main() {
    let user = User {
        name: "Michael".into(),
        last_name: "Jackson".into(),
        goes_first: project_member!(name),
    };
    println!("first: {}", user.first());
}
1 Like

To add to this, member pointers are “necessary” in C++ in order to support features like virtual inheritance. It might just be an offset, but it also might involve a vtable lookup. Thus the equivalent in Rust would actually be closer to fn(&User) -> &String.

There's no direct support for this, but if you really do have a reason to need to select between fields, the best way would be with an enum which you implement a method to project the primary reference with. But like any getter, be aware that means borrowing the whole structure.

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.