Can I delete assignment operator like C++?

In Rust, I have some code like this:

struct Foo {
    f1: OnceCell<i32>,
}

I want the f1 to be set only once, but it can be hacked:

    let mut once_cell = OnceCell::new();
    once_cell.set(1).unwrap();
    // in order to reset, assign a new `OnceCell` to `once_cell`
    once_cell = OnceCell::new();
    once_cell.set(2).unwrap();

I want to know if I can forbid the assignment for variable once_cell, just like in C++:

class Foo {
 public:
  Foo &operator=(Foo &&) = delete;
  Foo &operator=(Foo &) = delete;
  int i = 0;
};

class Bar {
 public:
  Foo foo;
};

int main() {
  Foo foo;
  Bar bar;
  Bar bar2;
  Foo foo2;
  bar.foo = foo2;
}

= in Rust is always just a memcpy. There's no operator to delete, since it's not customizable.

3 Likes

Then, in my case, how do you prevent this hack?

A simple solution is to leave f1 as a private struct field, and only make it readable/writable from accessor methods. But you generally cannot stop this from happening somewhere, a caller could always overwrite a variable that holds a Foo for example.

4 Likes

What's the XY here? The state can be reset without assignment if owned (or mutably borrowed), by the way.

2 Likes

Yes, that's why I didn't mention the "hide field" way, it's not 100% safe from hacking. But I think I will finally do this, just ask if there's a better solution.

Using OnceCell is just to make my example more reasonable, in my case I won't use it and I won't let the user reset the value.

I'm trying to say your example isn't giving enough information to offer any real advice. Unless I just take the subject as the entirety of the question, in which case the answer is "no". (And I'd consider something doing it's darnedest to prevent that to be the hack.)

Since you say you're not even going to use it, presumably you have an actual goal in mind and have misunderstood what OnceCell means. My best guess is you want a singleton, but Rust has no direct language support for that (and one uses things privacy and runtime checks instead). But with so little information, that guess is a shot in the dark.

3 Likes

Hi, I think it is not that complicated, I do want something that it's value cannot be changed once set. I write my custom type in C++, and when porting to Rust, I will do so as well. The problem is as the example illustrate: I'm trying to prevent user to replace the value by assigning a new one to it. I'm sorry if using OnceCell in the example make it confusing, since I just know this type hours ago, and may misunderstand what OnceCecll means.

The design pattern seems wired(I can explain, but it's a very long story), but believe me I know what I'm doing, and the singleton won't help here :smile:

OnceCell works that way when you put it in a static, basically. In that context, you can't replace it or get a &mut to it. There's no owner.

I don't think there's any way to do what you want. If I own two Foo I can always swap them or whatever, period.

4 Likes

"There's no way to do it" is also an answer :smile: I have a feeling in mind that it can't be done in Rust for now, just ask if there's a chance.

1 Like

Why are you concerned about hacking here? Rust doesn’t provide any serious security boundaries or sandboxing for untrusted code— All of Rust’s safety tools are designed to minimize the probability of accidental misuse by a well-intentioned programmer, but they won’t prevent a determined attacker from doing bad things. Especially if that attacker gets to link their own code into your binary.

If you can’t rely on the downstream programmer to be acting in good faith, you should look into some real sandboxing approaches, like WASM.


It’s hard to tell from your description, but I think you might be looking for something like Arc<OnceCell> here. It won’t prevent the user from making a new value, but that new value won’t affect other users— They will continue to have and use their own references to the original.

1 Like

Hi guys, l'm very thankful that you experts want my intention to do so in case of I'm asking an X/Y problem or have better solution, but shall we ignore some seriously expression like "100% safe" I said and this weird example which is of cause being considered as an ill design, and focus on the question itself :rofl:? Which is that does Rust have the same feature as C++ to disallow assignment, which currently is a "no" answer. :blush:

Rust does have such a feature, but it is quite different from C++’s: If you want to prevent a place from being assigned into, simply don’t provide mutable access to that place. Generally, this is accomplished via a privacy barrier which only shared references are allowed to pass through.

4 Likes

Feel free to ignore me then :sweat_smile:, but in case you pick up the Arc idea (which is pretty good) and run with it: you'll have to increment the strong count (and probably leak the value), or stash a clone somewhere or something, if you want to be sure get_mut doesn't work. (And if leaking is okay, you could also just use a &'static OnceCell or just a &'static Value I think.)

(With both those approaches, people could clone the field and it would be shared among more than one owner, though... and to prevent that, you're back to newtypes and privacy etc.)

3 Likes

I would fully agree that Rust has no feature to disallow assignment in the same flavor as C++ works. Of course assignment itself operates very differently between those two languages, too, but that’s a separate discussion. In case you’re not too interested in discussions far removed from the narrow “does Rust have a direct analogue of this C++ feature?” question, feel free to mark an appropriate response as “solution” to more clearly indicate that you consider your original question answered :slight_smile:

1 Like

When you say you're trying to prevent "the user" from changing f1, do you mean the owner of Foo, or somebody being provided a reference to Foo?

The owner can replace Foo in C++ even if you delete the assignment operator:

  Foo foo;
  foo.~Foo();
  new (&foo) Foo();

Rust will prevent assigning a new value to foo by the owner if foo is visible to anybody else (has been borrowed).

If you want the owner to prevent himself from replacing the value, simply don't mark the variable mut.

Somebody having a shared reference &Foo can't replace f1.

8 Likes

I never know I can use new in place like this, learns every day.

1 Like

There are also various other ways you can force a write to a Foo in C++, i.e. by using reinterpret_cast. If you stick only to safe Rust, you actually have more tools at your disposal to prevent unintended writes than you would in C++...

1 Like