Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking issue for unsized tuple coercion #42877

Open
qnighy opened this issue Jun 24, 2017 · 9 comments
Open

Tracking issue for unsized tuple coercion #42877

qnighy opened this issue Jun 24, 2017 · 9 comments
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC S-tracking-design-concerns Status: There are blocking design concerns. S-tracking-needs-summary Status: It's hard to tell what's been done and what hasn't! Someone should do some investigation. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@qnighy
Copy link
Contributor

qnighy commented Jun 24, 2017

This is a part of #18469. This is currently feature-gated behind #![feature(unsized_tuple_coercion)] to avoid insta-stability.

Related issues/PRs: #18469, #32702, #34451, #37685, #42527

This is needed for unsizing the last field in a tuple:

#![feature(unsized_tuple_coercion)]
fn main() {
    let _: &(i32, [i32]) = &(3, [3, 3]) as _;
}

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=5f93d1d7c6ee0e969df53c13e1aa941a

@Mark-Simulacrum Mark-Simulacrum added B-unstable Blocker: Implemented in the nightly compiler and unstable. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Jun 24, 2017
@Mark-Simulacrum Mark-Simulacrum added the C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC label Jul 22, 2017
@steveklabnik
Copy link
Member

Triage: no changes

@joshtriplett joshtriplett added the S-tracking-design-concerns Status: There are blocking design concerns. label Jan 26, 2022
@Mark-Simulacrum Mark-Simulacrum added the S-tracking-needs-summary Status: It's hard to tell what's been done and what hasn't! Someone should do some investigation. label Jan 26, 2022
@Mark-Simulacrum
Copy link
Member

T-lang discussed this in a backlog bonanza meeting today, and noted several concerns that should be addressed prior to moving forward:

  • Is there active usage/desire for this feature?
  • Tuple destructuring and/or combining may conflict with particular decisions made here (for a subset of types) -- which merits at least a more thorough exploration to make sure this concern is assessed (it may not be fully accurate).

@dhardy
Copy link
Contributor

dhardy commented Jan 27, 2022

I subscribed to this because I wanted to use it once... but didn't because it's unstable and not too hard to work around. I suspect the same goes for many non-trivial projects. Searching GitHub shows 1391 code results, most of which appear to be tests. There are a few uses on the first page: https://github.com/aschaeffer/inexor-rgf-application, https://github.com/aschaeffer/rust-ecs-poc, https://github.com/libdither/disp, https://github.com/icostin/halfbit (feature = "nightly"); everything after that appears to be tests (I checked 10 more pages).

In short: some possible utility, but not a priority.

@mleonhard
Copy link

I wish to use this in safina-executor library. The library currently uses this type for async tasks:

Arc<Mutex<dyn Future<Output = ()> + Send + Unpin>>

(v0.3.2 lib.rs:306)

I am modifying it to remember when a task has returned Polling::Ready and avoid polling it again.

I would like to use the following type, but it depends on this unsized_tuple_coercion feature:

Arc<Mutex<(bool, dyn Future<Output = ()> + Send + Unpin)>>

Instead, I will use this type, which requires two allocations per task:

Arc<Mutex<Option<Box<dyn Future<Output = ()> + Send + Unpin>>>>

This is OK. Async tasks are generally long-lived data structures. Therefore, the extra allocation is a very small added cost.

This Option version has a benefit: it lets us drop the completed future right away. With the bool version, the future drops after its last waker is dropped. Some applications may use timeouts that keep a waker for the duration of the timeout. That could keep future enclosures around for minutes, and increase memory pressure.

@SimonSapin
Copy link
Contributor

To use bool on stable, does using a struct type instead of a tuple work?

@mleonhard
Copy link

@SimonSapin A struct works!

struct Task<T: ?Sized> {
    completed: bool,
    fut: T,
}
impl<T> Task<T> {
    pub fn new(fut: T) -> Arc<Mutex<Self>> {
        Arc::new(Mutex::new(Task {
            completed: false,
            fut,
        }))
    }
}

impl Executor {
//...
    pub fn spawn_unpin(self: &Arc<Self>, fut: impl (Future<Output = ()>) + Send + Unpin + 'static) {
        let task = Task::new(fut);
        let weak_self = Arc::downgrade(self);
        self.async_pool.schedule(move || poll_task(task, weak_self));
    }
}

I think I'm going to keep the Option<Box<_>> version because of the memory savings I mentioned above. Using an enum didn't work:

enum Task<T: ?Sized> {
    None,
    Some(T),
}
// error[E0277]: the size for values of type `T` cannot be known at compilation time

@mleonhard
Copy link

I just realized that the None and Some enum variants must be the same size. So the enum version wouldn't save memory used directly by the Future. It would save only heap-allocated memory owned by the Future. A completed future from an async closure cannot contain own any heap-allocated memory because it drops all variables before returning and completing. Other types that implement Future may have heap-allocated memory, but those types are rarely used as tasks. Therefore, an enum alone provides almost no memory savings because it saves only heap-allocated memory, and completed tasks have none of that.

@QuineDot
Copy link

There's an argument to be made for consistency. You can unsize coerce, deconstruct, and otherwise match against custom tuple types already. You can also do all these things on stable with built-in tuples too, given a touch of unsafe. 1

Footnotes

  1. I do recognize that's still unsound without offset_of. On the other hand, there's no other reasonable layout in this case.

@dhardy
Copy link
Contributor

dhardy commented Feb 15, 2023

@QuineDot no you can't. You are merely using references to unsized types.

However, this edited version works (avoids deconstruction of the unsized parameter, or more precisely merely avoids creating a let binding to the unsized part).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC S-tracking-design-concerns Status: There are blocking design concerns. S-tracking-needs-summary Status: It's hard to tell what's been done and what hasn't! Someone should do some investigation. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

8 participants