{"id":103677,"date":"2020-04-16T07:00:00","date_gmt":"2020-04-16T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103677"},"modified":"2020-04-15T21:04:40","modified_gmt":"2020-04-16T04:04:40","slug":"20200416-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200416-00\/?p=103677","title":{"rendered":"Why can&#8217;t you return an <CODE>IAsync&shy;Action<\/CODE> from a coroutine that also does a <CODE>co_await<\/CODE>?"},"content":{"rendered":"<p>Suppose you&#8217;re writing a coroutine, and the last thing you do is call another coroutine which has exactly the same signature as your function. You might hope to be able to pull off some sort of tail call elimination.<\/p>\n<pre>IAsyncAction DoSomethingAsync(int value);\r\n\r\nIAsyncAction MySomethingAsync(int value)\r\n{\r\n  auto adjusted_value = adjust_value(value);\r\n  return DoSomethingAsync(adjusted_value);\r\n}\r\n<\/pre>\n<p>If there are no <code>co_await<\/code> or <code>co_return<\/code> statements in your function, then it is not compiled as a coroutine, and you can just propagate the <code>IAsync\u00adAction<\/code> as your own return value.<\/p>\n<p>But if you use <code>co_await<\/code> or <code>co_return<\/code>, then your function becomes a coroutine, and propagation doesn&#8217;t work:<\/p>\n<pre>IAsyncAction MySomethingAsync(int value)\r\n{\r\n  auto adjusted_value = co_await AdjustValueAsync(value);\r\n  return DoSomethingAsync(adjusted_value); \/\/ doesn't compile\r\n}\r\n<\/pre>\n<p>Instead, you have to <code>co_await<\/code> the final coroutine.<\/p>\n<pre>IAsyncAction DoSomethingTwiceAsync(value)\r\n{\r\n  auto adjusted_value = co_await AdjustValueAsync(value);\r\n  co_await DoSomethingAsync(adjusted_value);\r\n}\r\n<\/pre>\n<p>Why can&#8217;t you just propagate the final coroutine as the return value of your own coroutine?<\/p>\n<p>You can look at it in terms of the mechanics of <code>co_await<\/code>: The caller is going to <code>co_await Do\u00adSomething\u00adTwice\u00adAsync()<\/code>, which means that they are going to obtain an awaiter for <code>IAsync\u00adAction<\/code> and hook up their continuation to it. That awaiter is going to be managing the <code>IAsync\u00adAction<\/code> that <code>Do\u00adSomething\u00adTwice\u00adAsync<\/code> returns, which is not the same as the <code>IAsync\u00adAction<\/code> that the inner <code>Do\u00adSomething\u00adAsync<\/code> returns.<\/p>\n<p>Or you can look at it in terms of time travel: The transformation of <code>Do\u00adSomething\u00adTwice\u00adAsync<\/code> into a coroutine causes the function to return an <code>IAsync\u00adAction<\/code> at the point of the first suspension, whcih is at the <code>co_await Adjust\u00adValue\u00adAsync()<\/code> call. When the function performs the <code>co_await<\/code>, it returns an <code>IAsync\u00adAction<\/code> that represents the remainder of the coroutine. The code that calls <code>Do\u00adSomething\u00adAsync<\/code> hasn&#8217;t run yet, and consequently its <code>IAsync\u00adAction<\/code> does not yet exist. When the coroutine resumes, it eventually gets around to calling <code>Do\u00adSomething\u00adAsync<\/code> and obtains an <code>IAsync\u00adAction<\/code>. But it&#8217;s far too late to return that as the return value of <code>Do\u00adSomething\u00adTwice\u00adAsync<\/code>; that function returned ages ago. You can&#8217;t go back in time and say, &#8220;Oops, sorry, that&#8217;s not the <code>IAsync\u00adAction<\/code> I wanted to give you. Use this one instead.&#8221;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You already returned. No take-backs.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-103677","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You already returned. No take-backs.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103677","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=103677"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103677\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=103677"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103677"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103677"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}