Const(pure)

The topic suggests

    const(pure) fn foo() {...}
    impl const(pure) Trait ...

similar to but more permissive than vanilla const:

  • can only call fn-s that are const(pure) themselves
  • no access to static variables
  • no assignments through references
  • no asm
  • ...
  • can dereference pointers and use heap

Hopefully implementation can follow in footsteps of const fn-s and traits.

Motivation

Future Possibilities

As written const(pure) fn-s have same limitation as const fn-s:

  • can modify mut local bindings
  • cannot modify through &mut references at all
  • so, no iterators, sorry

To solve this would require coloring all &mut references throughout the type system in two colors - unique immutable and unique mutable. One idea here is to mark lifetimes as const..

Because of the size of the change it's left out as a future possibility. Initially const(pure) fn-s would treat all &mut references as immutable.

Note

It is hoped present-day const fn-s/traits will be a subset of const(pure) fn-s and traits. However to leave more future possibilities open initially const(pure) and const can be marked separately: It simplifies things to say that vanilla const fn will always be a stronger requirement than const(pure) fn.

Objections

It is interesting why pure was dropped but the suggestion just keeps coming back..

As written, this is currently useless for anything that involves loops (no guaranteed TCO, and no mutation). But there is a larger problem: purity polymorphism.

Ideally Option::map would be pure if the function/closure that is passed in is pure. If this wasn't true it would be much harder to write readable Rust. This is similar to const, and so it will require similar solutions to const. Currently, the best solution is const Trait and ?const TraitBound. But these are wordy and can make writing correct generic core difficult and sometimes, unreadable. Adding pure exacerbates this problem, without sufficient motivation.

These are similar reasons to why pure was removed in the first place, and they should be addressed. const addressed these problems by providing sufficient motivation in compile time execution. Does pure provide this level of motivation. If not, I don't think it's worthwhile.

2 Likes

Actually const fn-s can modify local mut variables now. But as noted iterators can't be used without a large-scale change introducing

// either
struct S<'a, T>{ v : &'a const mut T }
// or
fn foo<const 'a, 'b>(a : &'a mut .., b : &b' mut ..) {..}

Such change would benefit vanilla const fn-s as well so might carry its weight.

Oh, snap. Indeed.. Well... One could do poor-man's polymorphism

pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U> { ... }
pub const(pure) fn mapPure<U, /* indicate F is const(pure) somehow here */ F: FnOnce(T) -> U>(self, f: F) -> Option<U> { ... }

but I agree that this is far from satisfying. Perhaps such extra methods could be added in popular cases as a stop-gap measure but one would indeed desire to be able to express purity polymorphism properly and then deprecate those methods.

Right, but you mentioned that const(pure) can't. But this is small potatoes, and doesn't really matter right now. Purity polymorphism is more important.

As you mentioned, this isn't satisfying. This needs some way to hook into the trait system. If we're copying const, then we would need const(pure) + Trait and ?const(pure) + TraitBound along side const + Trait and ?const + TraitBound. Which can make things hard to track.

Perhaps such extra methods could be added in popular cases as a stop-gap measure but one would indeed desire to be able to express purity polymorphism properly and then deprecate those methods.

I think we've denied proposals for additions that are known to be deprecated in the future.

Magnificent! You have just mapped the path forward:

  • guarantee that vanilla const fn will always stay a stronger requirement than const(pure) fn
  • which introduces just 3 flavors of fn-s/traits: regular/pure/const

To be clear, I'm not in favor of this approach. It increases complexity significantly, without sufficient motivation.

1 Like

Oh no, not another effect system, please.


First, let's see some non-toy examples as to why this would have advantages significant enough to be worth overhauling one of the fundamental parts of the type system. I don't think "make another proposal (that was just written) work" counts as significant motivation.