Pre-RFC: IntoView – Contextual Type Projection Inspired by Vulkan Language

This proposal introduces a new trait, IntoView, inspired by a feature in the Vulkan programming language (my own language — otherwise, you might think I'm talking about the Vulkan API). The core idea is to allow types to define a contextual view of themselves as another type, which can be implicitly used in argument positions.

Rust traits like Deref, AsRef, From, and Into are either limited in scope or require explicit invocation:

Deref only applies to method resolution and *expr, but not in function arguments (e.g. fn f(x: i32) wont accept T: Deref<Target=i32>).

AsRef and Into require explicit calls like .as_ref() or .into(), even when conversions are unambiguous.

Method resolution uses &self, &mut self, or self, but is bound to syntax and ownership semantics — not general conversion.

In Vulkan, IntoView trait (not an actual trait, but a protocol-like construct in Vulkan language) allows types to define how they present themselves as another type:

class Foo {
    val value: i32;

    impl IntoView<i32> {
        fn view(&self): i32 {
            self.value
        }
    }
}

fn take_i32(v: i32) { /* ... */ }

fn main() {
    val a = Foo(999);
    take_i32(a); // Automatically calls .view()
}

Rust Equivalent (hypothetical)

struct Foo(i32)

impl IntoView<i32> for Foo {
    fn view(&self) -> i32 {
        self.0
    }
}

fn take_i32(v: i32) { /* ... */ }

fn main() {
    let a = Foo(999);
    take_i32(a); // Automatically uses view()
}

The compiler would use view(&self), view(&mut self), or view(self) based on context and ownership, similar to how method resolution already works, but across argument expectations.

In addition to function arguments, IntoView can also enable automatic conversion of values during variable assignment:

struct Foo(u32);

impl IntoView<u32> for Foo {
    fn view(self) -> u32 {
        self.0
    }
}

fn main() {
    let a = Foo(999);
    let num: u32 = a; // compiler implicitly calls a.view();
}
1 Like

Does your programming language have a website or Github repository? Specially with some writeup describing this feature in the context of the Vulkan lang

I'm not having much luck searching for it on Google

2 Likes

Vulkan is not ready yet, its still in the language design phase. Ive already provided a detailed description of the trait functionality.

Implicit Conversions are a mistake. Rust learned from C++'s disasters. Deref coercion already pushes the limits of acceptable magic. Now you want whole new categories of hidden behavior? No Just no to debugging nightmare: "Why is my Food turning into an i32 here?" Because oh right, someone snuck in an Interview impl three crates deep

Ownership? What Ownership? Rust’s strict borrowing rules exist for a reason. Implicitly choosing between &self, &mut self, and self based on "context" is a recipe for invisible bugs and cursed lifetime errors

This Solves Nothing. We already have Deref, As Ref, Into, and From. If your API needs this much implicit voodoo, your design is bad. Write clearer code instead of demanding the compiler fix it for you. Variable Assignment Conversions? Seriously? let x: u32 = foo; silently calling foo.view() is the kind of "feature" that gets languages laughed out of production use. Rust isn’t Kotlin or Scala explicitness is a strength. This isn’t ergonomic, it’s irresponsible

14 Likes

Thanks for the passionate feedback. Its always fun when someone screams "C++ horror story" at the first sign of ergonomic sugar.

Lets clarify a few things:

This isnt C++-style implicit conversions. Theres no constructor madness or silent type juggling here. IntoView is opt-in, local, and predictable. You can even explicitly opt out—e.g., with #[no_auto_view]; the full details will be clearly defined in final RFC. Thats more control than C++ ever gave.

Deref coercion exists, and its widely used — because it improves ergonomics without sacrificing clarity. So lets not pretend Rust is some purity cult that never touches "magic". The goal is pragmatic clarity, not dogmatic minimalism.

"This solves nothing" — except the exact problem Im solving. The point of IntoView is composability and reducing boilerplate in domain-specific contexts. Not every API needs it, but some will become dramatically more elegant with it.

And yes, let x: u32 = foo silently calling foo.view() is only possible if you explicitly allow it. Thats called design, not voodoo.

Rust strength isnt that it forbids magic — its that it makes it explicit, opt-in, and controlled. Thats exactly what this proposal offers.

Oh, so now we're calling uncontrolled implicit behavior "ergonomic sugar"? Cute

"Opt-in, Local, and Predictable". Sure, until your dependency tree has 17 different IntoView impls, and suddenly x.do_thing() works when it shouldn’t. "But it’s opt-in" screams the dev who just inherited your codebase

"Deref Coercion Exists" and it’s already one of Rust’s most controversial features. "Hey, this other footgun exists, so why not add another?" isn’t the compelling argument you think it is

"Explicit, Opt-In Magic" Magic is never "explicit." The second this ships, libraries will abuse it, and you’ll be debugging "why does x suddenly view() into y?" at 3 AM

Rust’s success comes from resisting this exact creep of "just a little sugar." If you want implicit conversions, languages exist for that. Stop trying to turn Rust into Scala

1 Like

Youre right — "ergonomic sugar" can turn into "footguns" when its uncontrolled. Thats why IntoView is explicitly opt-in at every step: no global language change, no compiler magic, no silent auto-anything unless you write it.

And yes, libraries can abuse features. But thats true for every abstraction in Rust — from macros to unsafe to Deref. The answer isnt to avoid all abstraction, its to give developers tools with "safety valves :feris". Like #[no_auto_view]. Like limited scopes. Like opt-in derivations.

You keep referencing C++ and Scala. But youre ignoring the fact that Rust already embraced controlled coercion — Deref, From, TryFrom, etc. Are they controversial? Sometimes. But theyve also enabled expressive, safe code, and Rust is better for having them.

The fear that "youll be debugging view()" is valid — if IntoView were invisible. But its not. Theres nothing implicit at the type system level. Its as explicit as Deref, as local as trait bounds, and as controlled as you need it to be. And unlike Deref, you can forbid it per-function.

If youre arguing that all abstraction leads to chaos, then the only consistent position is "go write C". But Rust is about balancing safety. Thats not magic. Thats design.

I agree with @Alexanderr193 when it comes to the contents, but not the way it is written: This implicit view idea sounds like a really bad idea. Thankfully I believe the core rust developers are sensible enough to realise this. So there is no need for such agitated language.

It is better to engage people with differing opinions in a calm manner without accusing the other side. This applies to both of you, even if (especially when) the other person acts in a way you dislike.

Shouting matches don't lead to any changes of opinions. It just makes both side dig in further.

13 Likes

Okay

Even if opt-in, this introduces new mental overhead. Developers now need to check for IntoView impls when reasoning about function calls, adding to the "hidden control flow" problem Rust generally avoids

While IntoView would appear in documentation, unexpected automatic conversions could still lead to confusing behavior, especially when chained across dependencies. Rust's explicitness is one of its strengths here. Features like this tend to expand in scope over time. Even if initially limited, pressure to make it more pervasive (e.g., in stdlib) could lead to the same issues we see with Deref today

Between From/Into, as casting, and custom methods, Rust already provides ways to handle conversions explicitly. The tradeoff in clarity seems worth it compared to implicit behavior.

If IntoView becomes widely adopted, refactoring types could accidentally break downstream code in subtle ways, e.g., by introducing new IntoView impls

I worked on a code base that made heavy use of From impls. Even following that (where the calls are explicit) was difficult. There where really two big issues:

  • Rust analyser had issues with "goto implementation" not working to find the appropriate From when I tried to goto an into(), instead going to the blanket impl of Into for From.
  • When doing code review (in Azure Devops, but Github has the same issue) it was straight up impossible to follow.
3 Likes

I asked for more info about your language because features don't exist in isolation. Rust kind of has an ethos that explicit is better than implicit, and that's why, for example, it doesn't convert floats to ints implicitly. I'm not making a value judgement here, other languages make different tradeoffs and I think many people feel that Rust is too verbose (for example, I would love syntax sugar for both x.into() and x.to_owned())

In any case, for implicit conversion in function parameters specifically, Rust already has a trait for this, Into, using this idiom:

#[inline(always)]
fn take_i32(param: impl Into<i32>) {
    fn take_i32_inner(v: i32) {
        ...
    }

    take_i32_inner(param.into());
}

// both work
take_i32(Foo(999));
take_i32(999);

Having to define an inner non-generic function sucks, but if you don't do that, the compiler may uselessly monomorphize the function bodies, which may be a problem if the function is big. There's a crate that performs this transformation with a macro (momo) but approximately 0 people use it. rustc should automatically avoid monomorphization in this case, even if it applies only to Into, AsRef and AsMut (I think I read somewhere this was in the radar), and if it does, this could be simplified to

fn take_i32(param: impl Into<i32>) {
    ...
}

Which in my opinion looks great.

edit: I just looked up and polymorphization (the unstable feature that turned the previous snippet into something equivalent to the inner function idiom at the beginning) was removed: Delete current polymorphization implementation · Issue #810 · rust-lang/compiler-team · GitHub and, while there was an expressed desire to get this back into the compiler somehow, the tracking issue was closed as "not planned". Which is sad. But then you can just depend on momo and write

#[momo]
fn take_i32(param: impl Into<i32>) {
    ...
}

Unfortunately ~nobody does that.

5 Likes

IntoView proposal isnt meant to replace Into, From, or other explicit conversions. Rather, its meant to provide a contextual view of a type, which only gets used when receiving function explicitly opts in via its signature.

Your impl Into example works well, but requires a wrapper function to avoid unnecessary monomorphization — and even then, the ergonomics arent great. IntoView is aimed at situations where you want to write clear APIs without repeating boilerplate or forcing verbose conversions on caller, while still keeping conversions local and opt-in.

To be clear:

It wouldnt change default behavior.

It wouldnt allow conversions unless function explicitly supports them.

It complements, rather than replaces, existing Into/From ecosystem.

Feature is opt-in at every level, meaning its not imposed on developers. Its up to developer to introduce and use it. If a developer is using IntoView in an unexpected way, thats a code quality issue, not a problem with feature itself. Rust already has many similar features (like Deref, From, Into) that dont carry same mental burden, and this would be no different.

The idea isnt to introduce "hidden control flow", but to provide a way to implicitly convert types where it makes sense. While it may look like it could lead to unexpected conversions in the future, the proposal includes clear boundaries: its scoped, controlled, and there are existing mechanisms to opt-out. If its introduced with a careful design, it wouldnt evolve into something "we regret, as happened with Deref".

As for breaking downstream code, refactoring is always a risk, especially when introducing new conversions or changing API designs. But, much like with other conversions (e.g. From/Into), its up to the library authors to manage their APIs well. This feature enhances clarity in situations where explicit conversions of From/Into would create unnecessary boilerplate, and improves ergonomics without sacrificing control.

IntoView would be a tool — just like From and Into — that can be used or avoided as necessary. And if adopted carefully, it wont disrupt languages explicitness or cause kinds of problems.

Your examples don't reflect this.

fn take_i32(v: i32) { /* ... */ }

fn main() {
    let a = Foo(999);
    take_i32(a); // Automatically uses view()
}

Where did take_i32 and main opt in?

Deref coercion does happen for function arguments behind references.

4 Likes

Let me clarify: by "opt-in at every level" I mean that:

Types must explicitly implement IntoView, so nothing happens unless author of a type wants it.

Compiler will only call view() if type implements IntoView, and function signature matches one expected in IntoView implementation.

As for Deref: youre right that it only applies in method resolution and *expr. But this is exactly why IntoView is not trying to generalize Deref — separate channel of controlled contextual conversion, opt-in at both call site and impl site, to avoid implicit chaos.

Let’s be real. This idea is bad

"Opt-In" is a lie. Once this exists, libraries will abuse it. Today "oh, it's just for niche cases" and tomorrow "why doesn’t serde/tokio/anyhow use it?" Now you’re debugging implicit conversions in every codebase

The whole point of From/Into is that conversions are visible. IntoView makes them invisible at the call site - exactly the kind of "magic" Rust avoids

Debugging would be a nightmare. "Why does x work here but not there?" Because somewhere, somehow, an IntoView impl is (or isn’t) being picked up. Enjoy tracing that through 10 layers of dependencies

Want ergonomics? Improve Into/From with compiler optimizations

Need sugar? Write a macro

Don’t add language-level complexity for something a proc-macro could handle

2 Likes

The idea itself isn't bad per se. It can work, but it would require IDEs to work with it or a weaker type system. But I agree it doesn't belong in Rust.

Scala had implicit conversions (and C#), and they hampered readability quite a bit. This, combined with Traits weird type conversion and importing, would be make it nightmare for someone without autocomplete to understand. E.g. reading code on GIthub, Gitlab, or in text form.

5 Likes

Most of the use-cases of special language support for "view types" that I am aware of require them to be something that you can't really use a trait for without modifying the language at the deeper level. For example, something that allows the borrowchecker to reason about "partial borrows" across API boundaries. This makes a trait somewhat irrelevant for the discussion, I would think, as all traits lack a way to express this in today's Rust and it would need a language extension.

@Alexanderr193

Want ergonomics? Improve Into/From with compiler optimizations

I have no idea what "compiler optimizations" you could possibly mean?

I think From and Into are a pretty "mid at best" API design. Arguably, simply bad? They involve highly generic things that are bad for type inference, which is bad for ecosystem stability, as it makes it easy to break that inference by introducing new candidates or whatnot. It also lacks a meaningful contract for the conversion, as it is not really required to be lossless, cheap, or any other interesting property for a program to rest on. AsRef or Borrow are a bit more useful, as they have fairly obvious contracts ("they produce a reference"), so they see more actual use in std's actual API surface. Otherwise, we find From used a lot less than, say, FromIterator or FromStr, pointing to how the trait isn't really good enough for expressing lots of conversions in a useful way!

For these reasons, I believe thinking about alternatives to From and Into, especially for actual use in an API surface, should not be discouraged. This remains true even if I do not particularly enjoy the suggestion here. Please reflect on what you are actually trying to accomplish and what is actually needed to accomplish that before posting generic heat. Critique should be focused.

9 Likes

I mean, it's not like C++-style implicit conversions can happen without the author of a type wants it.

Yes, but since From and Into, Deref and AsRef won't be removed, we need to keep into consideration how they will all compose.

You don't want a feature that's basically done by current code, plus some minor code gen (like abovementioned momo).