modular / modular/modular

[Feature Request] Traits cannot express "returns a reference to a field of self"

Open
#6,907 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement mojo origins Team: Mojo Compiler
Dominant language
Mojo
Stars
29.8k
Forks
3.2k
PR merge metrics
No merged PRs in 30d

Description

Review Mojo's priorities
What is your request?

Allow a trait method to require "returns a reference into self", and allow an implementation to satisfy that requirement by returning a reference to one of its own fields.

Concretely, I'd like this pair to compile:

trait Container:
    def get(mut self) -> ref[self] List[Int]:
        ...

struct Boxes(Container):
    var items: List[Int]

    def __init__(out self):
        self.items = [1, 2, 3]

    def get(mut self) -> ref[self.items] List[Int]:
        return ref self.items

This does not yet work, but seems quite straightforward.

What is your motivation for this change?

Returning a reference to a field works fine on a plain struct, but becomes inexpressible the moment the method is required by a trait. The implementation has to fall back to returning by value, copying a collection that the caller only wanted to borrow.

So, the following compiles and returns 1.

struct Boxes:
    var items: List[Int]

    def __init__(out self):
        self.items = [1, 2, 3]

    def get(mut self) -> ref[self.items] List[Int]:
        return ref self.items

def main():
    var b = Boxes()
    ref got = b.get()
    print(got[0])

But as soon as we want to add more structure using a trait, we run into issues:

Try #1: Trait requires ref[self], impl provides ref[self.items]

trait Container:
    def get(mut self) -> ref[self] List[Int]:
        ...

struct Boxes:
    var items: List[Int]

    def __init__(out self):
        self.items = [1, 2, 3]

    def get(mut self) -> ref[self.items] List[Int]:
        return ref self.items

def main():
    var b = Boxes()
    ref got = b.get()
    print(got[0])

Same struct as above, now with struct Boxes(Container) and the trait from field 1:

error: cannot return 'self's origin, because it might expand to a RegisterPassable type
    def get(mut self) -> ref[self] List[Int]:
                ~~~~               ~~~~^~~~~
error: 'Boxes' does not implement all requirements for 'Container'
note: no 'get' candidates have type 'def(mut self: Boxes) thin -> ref[*[0,0]] List[Int]'
note: candidate declared here with type 'def(mut self: Boxes) thin -> ref[*[0,0].items] List[Int]'

Try #2: Trait and impl both use ref[self]

trait Container:
    def get(mut self) -> ref[self] List[Int]:
        ...

struct Boxes:
    var items: List[Int]

    def __init__(out self):
        self.items = [1, 2, 3]

    def get(mut self) -> ref[self] List[Int]:
        return ref self.items

def main():
    var b = Boxes()
    ref got = b.get()
    print(got[0])

But this does not work as well:

error: cannot return 'self's origin, because it might expand to a RegisterPassable type
    def get(mut self) -> ref[self] List[Int]:
                ~~~~               ~~~~^~~~~
error: cannot return reference with incompatible origin: 'origin_of(self.items)' vs 'origin_of(self)'
        return ref self.items

So there is no way through. The trait cannot name the implementer's fields, because it
does not know them; and it cannot name self either, since that spelling is rejected
on the trait declaration itself — note that this first error appears in both cases 2
and 3, independently of what the implementation does.

The only workaround I found is to give up the reference entirely:

def get(mut self) -> List[Int]:
    return self.items.copy()

Correct, but it copies the collection on every call. For a trait that exists precisely
to expose internal storage, that undercuts the point of having borrows.

Any other details?

All above was tested on Mojo 1.0.0 (ed45d567), Linux x86_64.

Related but distinct: modular/modular#6810 (binding a type parametrized with an origin in a
trait's associated alias).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the two trait and implementation examples from the issue on Mojo 1.0.0 (ed45d567), including the reported origin errors. Done means a trait can express a reference into self, an implementation can return a reference to its field, and the examples compile without copying the collection.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.