Iterable trait as function argument

I noticed an Iterable trait appeared in the nightly builds recently, and it looks like it’s getting quickly integrated into the stdlib.

I’d love to use it! … but how do you define a function with an argument whose type is bound by Iterable and also express type bounds on the iterated type?

The usual pattern of defining the type bound as Iterable[TheType] doesn’t seem to work here. Looks like Iterable is using an associated alias instead of a parameter? How do we express bounds on associated aliases in function arguments? I think the syntax in Rust for this is something like Iterable<IteratorType=TheType>. Does Mojo have something similar? Yet?

Hey @cuchaz
Unfortunately there’s no easy to do that as of now.
Ideally in the future maybe we could write something like

fn foo[I: Iterable](iter: I) requires __type_eq(I.IteratorType.Element, Int):
    ...

However, in the meantime your best bet is going to be using a rebind.

fn foo[I: Iterable](iter: I) -> List[Int]:
    var result = List[Int]()
    for element in iter:
        result.append(rebind[Int](element))
    return result^

The Iterator/Iterable API is still going to likely go through a handful of changes, but hopefully we will be able to make it easy to use w/out too much complexity of origins and the likes :upside_down_face:

Thanks! That seems like it should work, but unfortunately the compiler is crashing. Here’s a very simple repro case:

fn use_iterable[I: Iterable](iterable: I):
    for _ in iterable:
        pass

def main():
    var values = [1, 2, 3]
    use_iterable(values)

And the crash report:

Please submit a bug report to https://github.com/modular/modular/issues and include the crash backtrace along with all the relevant source codes.
[471266:471267:20250919,103218.892011:ERROR directory_reader_posix.cc:42] opendir /.../.pixi/envs/default/share/max/crashdb/attachments/c48edc56-22c9-4893-b48b-afefff8b78a4: No such file or directory (2)
 #0 0x0000574cf4f4ccab llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/.../.pixi/envs/default/bin/mojo+0x657bcab)
 #1 0x0000574cf4f4a4aa llvm::sys::RunSignalHandlers() (/.../.pixi/envs/default/bin/mojo+0x65794aa)
 #2 0x0000574cf4f4d857 SignalHandler(int, siginfo_t*, void*) (/.../.pixi/envs/default/bin/mojo+0x657c857)
 #3 0x00007f444ee45330 (/lib/x86_64-linux-gnu/libc.so.6+0x45330)
 #4 0x00007f444ef2725d syscall ./misc/../sysdeps/unix/sysv/linux/x86_64/syscall.S:38:0
 #5 0x00007f4452c80b43 SignalHandler(int, siginfo_t*, void*) (/.../.pixi/envs/default/bin/../lib/libAsyncRTMojoBindings.so+0x51b43)
 #6 0x00007f444ee45330 (/lib/x86_64-linux-gnu/libc.so.6+0x45330)
 #7 0x0000574cf1851979 M::KGEN::IREvaluator::evaluateGetWitnessAttr(M::KGEN::GetWitnessAttr) (/.../.pixi/envs/default/bin/mojo+0x2e80979)
 #8 0x0000574cf1850e56 M::KGEN::IREvaluator::evaluateExpression(ContextuallyEvaluatedAttrInterface) (/.../.pixi/envs/default/bin/mojo+0x2e7fe56)
 #9 0x0000574cf1cd2721 M::KGEN::ParameterEvaluator::doReplace(mlir::Attribute, unsigned long) (/.../.pixi/envs/default/bin/mojo+0x3301721)
#10 0x0000574cf16b51cc std::conditional<std::is_base_of_v<mlir::Type, mlir::Attribute>, mlir::Type, mlir::Attribute>::type M::KGEN::ParameterReplacer<M::KGEN::ParameterEvaluator>::replaceImpl<mlir::Attribute>(mlir::Attribute, unsigned long) (/.../.pixi/envs/default/bin/mojo+0x2ce41cc)
#11 0x0000574cf1857df6 M::KGEN::IREvaluator::concretizeParameterExpr(M::KGEN::ImplNode*, mlir::Location, mlir::Attribute) (/.../.pixi/envs/default/bin/mojo+0x2e86df6)
#12 0x0000574cf1828c7f M::KGEN::Elaborator::processCallOp(M::KGEN::ImplNode*, M::KGEN::GeneratorUserOpInterface) (/.../.pixi/envs/default/bin/mojo+0x2e57c7f)
#13 0x0000574cf182bbbf M::KGEN::Elaborator::processImplNode(M::KGEN::ImplNode*) (/.../.pixi/envs/default/bin/mojo+0x2e5abbf)
#14 0x0000574cf184800d void llvm::detail::UniqueFunctionBase<void>::CallImpl<M::KGEN::Elaborator::scheduleImplNode(M::KGEN::ImplNode*)::$_0>(void*) (/.../.pixi/envs/default/bin/mojo+0x2e7700d)
#15 0x0000574cf1d33c53 void (anonymous namespace)::WorkQueueThread::runItemsImpl<(anonymous namespace)::WorkQueueThread::runOnThread()::$_0, (anonymous namespace)::WorkQueueThread::runOnThread()::$_1>((anonymous namespace)::WorkQueueThread::runOnThread()::$_0, (anonymous namespace)::WorkQueueThread::runOnThread()::$_1, bool, llvm::StringLiteral, llvm::StringLiteral) (/.../.pixi/envs/default/bin/mojo+0x3362c53)
#16 0x0000574cf1d33997 (anonymous namespace)::WorkQueueThread::runOnThread() (/.../.pixi/envs/default/bin/mojo+0x3362997)
#17 0x00007f4452d7b198 execute_native_thread_routine /home/conda/feedstock_root/build_artifacts/gcc_compilers_1753899950008/work/build/x86_64-conda-linux-gnu/libstdc++-v3/src/c++11/../../../../../libstdc++-v3/src/c++11/thread.cc:106:5
#18 0x00007f444ee9caa4 start_thread ./nptl/pthread_create.c:447:8
#19 0x00007f444ef29c3c clone3 ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:80:0
mojo crashed!
Please file a bug report.
/.../.pixi/envs/default/bin/mojo: error: Segmentation fault (core dumped)

I can make a bug report on github too, if you like.

Edit: version = 0.25.6.0.dev2025091805

This looks bad. Do you mind filing an issue on GH?

Apologies I forgot to respond!
This is actually a known bug that has to do with list literals. (I’m pretty sure it’s getting fixed soon).
If you update the code to not use a literal it should work!

fn use_iterable[I: Iterable](iterable: I):
    for _ in iterable:
        pass

def main():
    var values = List[Int](1, 2, 3)
    use_iterable(values)

Looks like I’m getting the same crash even with a List instance, rather than a literal. My original encounter with this crash was a much more complicated case, and simplifying to literal usage didn’t change the result, so this seems consistent at least. This is on the newer 25.7.0.dev2025092205btw.

Edit: do you still want a GitHub issue?

@cuchaz - Yes please! Thanks for finding this and it would be great to make a GitHub issue for this :slight_smile: Internally we can figure out if this is already tracked or not and hopefully get a fix for it. :folded_hands:

You got it!

3 Likes