Skip to content

Tracking issue for try_array_from_fn #89379

Open

Description

@c410-f3r

View all comments

Provides the common use-case of creating custom fallible arrays in a reliable and unified way. Particularly useful for elements that don't implement Copy, Clone or Default.

Public API

// Unstable
fn try_from_fn<E, F, T, const N: usize>(cb: F) -> Result<[T; N], E>
where
    F: FnMut(usize) -> Result<T, E>;

Steps / History

Unresolved Questions

Activity

  1. added
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    T-libs-api[DEPRECATED; DO NOT USE]
    on Sep 29, 2021
  2. dtolnay commented on Nov 2, 2021

    @dtolnay
    Member

    I am not certain the currently implemented signature of try_from_fn is the right one. The most similar thing to this currently in the standard library is https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.try_for_each, where the return type is a generic R based on a Try trait impl, instead of hardcoded to Result. Can try_from_fn be done the same way?

  3. scottmcm commented on Nov 27, 2021

    @scottmcm
    Member

    @dtolnay I've submitted a PR to update array::try_from_fn to be generic over Try types: #91286

    (As well as other related methods like Iterator::try_find #63178 that hit similar questions.)

  4. scottmcm commented on Nov 28, 2021

    @scottmcm
    Member

    My 2¢ on the usize parameter question:

    • There's clearly a bunch of cases where it's handy, as @‍c410-f3r showed in Change array_from_fn signatures #90505 (comment)
    • It's easy to ignore it if you don't need it, like in array::from_fn(|_| value.clone()).
    • But tracking it yourself if the function doesn't when you do need it is a pain. ι being from_fn(identity) is so much tidier than { let mut i = 0; from_fn(|| { let temp = i; i += 1; temp }) } or whatever.
    • Unlike with iter::from_fn, a usize fundamentally can't overflow in array::from_fn. And iter::from_fn's closure must return an Option, so its signature will never match array::from_fn. (Though I guess array::try_from_fn can take -> Option closures, but with different semantics.)
    • Making it generic opens so many questions (like overflow) that I just don't see it happening in core. It could be done with Default + Step, but that's arguably a misuse of Default, and it also allows things like array::from_fn::<i8, 200> which feels wrong. I don't see core importing num_traits::Unsigned any time soon.
    • Making it generic would make it harder to ignore when you don't need it. It would be a shame to have to write array::from_fn(|_: usize| value.clone()), for example. It could potentially allow from_fn(|()| value.clone()), but that's non-obvious and makes the trait question even harder. (().. isn't an iterator, and I hope that stays the case.)
    • enumerate is precedent for "sometimes we only offer the usize version", so it's not unusual to only support usize here.
    • With TryInto in the prelude it's not that bad to get another type, and the range is obvious to LLVM in the monomorphization, so the checks easily optimize away.
    • It's almost certainly free to have the index. It's actually being maintained in the guard in collect_into_array anyway to know which things to drop. Not to mention that it has to write into the array, where if that's done with addressing modes like ptr [edx + 4*eax] then the counter's already there too. (And things'll probably get inlined enough anyway that it'd be easy to remove if unused. The only way it wouldn't would be if the closure if complex, but then the cost of 1 LEA is unnoticeable.)

    So personally I think array::from_fn is right how it is, for all the reasons it ended up this way in the initial PR. And I'd like to see it move forward for stabilization. That's not up to me, though.

    I don't think array::try_from_fn is ready yet, though, so I don't know how procedurally libs-api would like to go, here. Usually it FCPs in the tracking issue, but there can only be one FCP per issue/PR, so maybe for a partial one it'd need a separate issue or a stabilization PR in which to make the decision?

  5. yoshuawuyts commented on Dec 1, 2021

    @yoshuawuyts
    Member

    I am not certain the currently implemented signature of try_from_fn is the right one. The most similar thing to this currently in the standard library is https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.try_for_each [...]

    Seconding this. I'm trying to collect an iterator into an array, for which I'd like to write:

    fn parse() -> Option<()> {
        let arr = array::try_from_fn(|_| self.iter.next())?;
        // ...
    }

    But because try_from_fn is hard-coded to return a Result, this does not work. Having an R: Try bound would solve this issue.

  6. scottmcm commented on Dec 1, 2021

    @scottmcm
    Member
  7. scottmcm commented on Dec 3, 2021

    @scottmcm
    Member

    I hate to re-bikeshed, but I stumbled on a possibility which I didn't see mentioned in #75644

    I'm making a PR for array::repeat, to handle simple things like @leonardo-m's repeat(vec![]). But in doing that I noticed that there's not only iter::repeat -- which I was copying for array::repeat -- but there's also iter::repeat_with.

    So I went to make array::repeat_with, but of course it's essentially identical to this tracking issue's array::from_fn. It's a much better parallel than iter::from_fn, as iter::repeat_with returns an "infinite" iterator and thus the closure is just -> Item (not from_fn's -> Option<Item>), like how the closure for creating an array is -> Element.

    So a potential new coat of paint for the constructors could be

    • array::repeat for creating by cloning, like repeat(vec![1]).
    • array::repeat_with for creating by calling a closure, like repeat_with(|| Vec::with_capacity(10)).
    • array::try_repeat_with for creating by a fallible closure.

    I don't know if this makes me feel differently about the usize argument. Allowing repeat_with(VecDeque::new) does feel somewhat more expected to me than from_fn(VecDeque::new), but I also still agree with the arguments in my previous comment. Of course, having both is also a possibility -- just like there's both iter::from_fn and iter::repeat_with, despite them being rather similar. Perhaps for nightly it would be reasonable to have both, with notes on their tracking issues to think about the other one if considering for stabilization?

  8. c410-f3r commented on Dec 4, 2021

    @c410-f3r
    ContributorAuthor

    The two most important things about these functions are (1) Provide an unified way for creating custom arrays (it is common enough for std) and (2) Take off the user burden of having to deal with unsafe.

    That said, I will personally support any proposal that will move any function or method towards stabilization as soon as possible. So if desired feel free to create repeat-like variants, maintain both versions or even close this issue.

    Although some recent development is happening in this field, sometimes I think that custom array constructors will take, if ever, as long as try_reserve or more.

  9. elichai commented on Feb 10, 2022

    @elichai
    Contributor

    Since I've seen this, I've written [(); N].map(|_| ...) dozens of times, some required the int and some didn't.
    So I'd love to see this stabilized :)

  10. added a commit that references this issue on May 22, 2022
  11. yoshuawuyts commented on Jul 5, 2022

    @yoshuawuyts
    Member

    Now that #94119 has been merged to stabilize this, can we close this issue?

  12. andylizi commented on Jul 5, 2022

    @andylizi
    Contributor

    Now that #94119 has been merged to stabilize this, can we close this issue?

    Only from_fn is stabilized, try_from_fn is still unstable. The latter is blocked on #84277.

  13. safinaskar commented on Jul 17, 2022

    @safinaskar
    Contributor

    Please, also add this functions:

    fn concat_arrs<T, const N: usize, const M: usize>(a: &mut ([T; N], [T; M])) -> &mut [T; N + M]; // Without coping, same for 3-tuples, 4-tuples, etc
    fn concat_arr_of_arrs<T, const N: usize, const M: usize>(a: &mut [[T; N]; M]) -> &mut [T; N * M];
  14. 10 remaining items

  15. c410-f3r commented on Feb 7, 2023

    @c410-f3r
    ContributorAuthor

    As @scottmcm said, it is necessary to have stable constant closures in order to "constify" array::from_fn in stable toolchains, which is probably going to happen in the next few years or so. See #106003.

    I think it is already possible to at least "constify" the hidden underlying machinery but I am not totally sure.

  16. rdrpenguin04 commented on May 28, 2023

    @rdrpenguin04

    Is there any reason this isn't stable yet?

  17. NathanRoyer commented on Jul 20, 2023

    @NathanRoyer

    Hello,

    let mut parts = "Mary   had a small lamp".split_ascii_whitespace();
    let parts: [&str; 5] = from_fn(|_i| parts.next().unwrap());
    // produces: ["Mary", "had", "a", "small", "lamp"]

    This works, but the documentation doesn't state that the callback parameter will grow upwards, so I'm not confident to use this code.
    One day this could end up producing ["lamp", "small", "a", "had", "Mary"], if the implementation decided to "push" values from right to left, with a cb param going from 4 to zero; nothing says otherwise in the doc.

    Is it possible that one day, we need the callback parameter to go in decreasing order?
    If not, I think it would be nice to add a word about this in the documentation.

  18. rdrpenguin04 commented on May 25, 2024

    @rdrpenguin04

    Are there any open questions about this? If not, could someone start an FCP?

  19. changed the title [-]Tracking issue for `array_from_fn` and `try_array_from_fn`[/-] [+]Tracking issue for `try_array_from_fn`[/+] on May 25, 2024
  20. c410-f3r commented on May 25, 2024

    @c410-f3r
    ContributorAuthor

    try_trait_v2 needs to be stabilized first and that probably won't happen in the near future. I am personally betting in 4 years or more (if ever).

  21. rdrpenguin04 commented on May 26, 2024

    @rdrpenguin04

    Why does this depend on try trait necessarily? try_for_each is already in the standard library.

  22. c410-f3r commented on May 26, 2024

    @c410-f3r
    ContributorAuthor

    Why does this depend on try trait necessarily?

    Well, the original function returned Result but you can see more context about the use of Try in #90505, #91286 and
    #94119 (comment).

    try_for_each is already in the standard library.

    EDIT: Oh I think it is because of concerns around the Residual associated type and try_for_each doesn't use that.

  23. scottmcm commented on May 27, 2024

    @scottmcm
    Member

    @c410-f3r is right -- the distinction is the use of the Residual mechanism, which has no stable use in the library yet

    See #94119 (comment) and #94119 (comment)

    Same questions as, for example, Iterator::try_find (#63178).

  24. MiSawa commented on Jun 27, 2025

    @MiSawa
    Contributor

    Hello,

    let mut parts = "Mary had a small lamp".split_ascii_whitespace();
    let parts: [&str; 5] = from_fn(|_i| parts.next().unwrap());
    // produces: ["Mary", "had", "a", "small", "lamp"]

    This works, but the documentation doesn't state that the callback parameter will grow upwards, so I'm not confident to use this code. One day this could end up producing ["lamp", "small", "a", "had", "Mary"], if the implementation decided to "push" values from right to left, with a cb param going from 4 to zero; nothing says otherwise in the doc.

    Is it possible that one day, we need the callback parameter to go in decreasing order? If not, I think it would be nice to add a word about this in the documentation.

    Noticed that the ascending order is now guaranteed in Rust 1.88 🎉 #139061

  25. bratorange commented on Jan 29, 2026

    @bratorange

    When using this feature I got an exception:
    EXC_BAD_ACCESS (code=2, address=0x171ae72f0)

    The code in question:
    pub static FFT_DATA: Lazy<[Mutex<[f32; FREQ_BINS ]>; RMS_BUFFER_SIZE]> =
    Lazy::new(|| array::from_fn(|_| {
    Mutex::new([0f32; FREQ_BINS])
    }));

    Environment
    OS: macOS 15.6.1
    rustc 1.90.0 (1159e78 2025-09-14)
    binary: rustc
    commit-hash: 1159e78
    commit-date: 2025-09-14
    host: aarch64-apple-darwin
    release: 1.90.0
    LLVM version: 20.1.8

  26. rdrpenguin04 commented on Feb 16, 2026

    @rdrpenguin04

    When using this feature I got an exception: EXC_BAD_ACCESS (code=2, address=0x171ae72f0)

    The code in question: pub static FFT_DATA: Lazy<[Mutex<[f32; FREQ_BINS ]>; RMS_BUFFER_SIZE]> = Lazy::new(|| array::from_fn(|_| { Mutex::new([0f32; FREQ_BINS]) }));

    Environment OS: macOS 15.6.1 rustc 1.90.0 (1159e78 2025-09-14) binary: rustc commit-hash: 1159e78 commit-date: 2025-09-14 host: aarch64-apple-darwin release: 1.90.0 LLVM version: 20.1.8

    That looks like it's with the general from_fn constructor. Would you be willing to open a separate issue?

  27. added
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    and removed
    T-libs-api[DEPRECATED; DO NOT USE]
    on Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-arrayArea: `[T; N]`C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions