洞箫 發表於 2025-4-10 19:09:00

rust学习二十.13、RUST的函数指针fn和匿名函数

<p>函数指针是一个好东西。</p>
<h1>一、简述</h1>
<p>函数指针就是执行一段函数代码的指针。</p>
<p>根据官方的说法,函数指针实现了FnOnce,FnMut,Fn特质。</p>
<p>由于函数指针是一种数据类型,所以rustc允许它作为函数/方法的参数,这样就给程序设计添加了不少的灵活性.</p>
<p>我估摸着,rust设计者就是为了让rust能够适应潮流:在函数/方法中直接传递匿名函数/闭包</p>
<p>一个典型的带有函数指针的rust函数定义如下:</p>
<div style="color: rgba(0, 0, 0, 1); background-color: rgba(255, 255, 255, 1); font-family: Consolas, &quot;Courier New&quot;, monospace; font-weight: normal; font-size: 14px; line-height: 19px; white-space: pre">
<div><span style="color: rgba(0, 0, 255, 1)">fn</span><span style="color: rgba(0, 0, 0, 1)"> do_twice(f: </span><span style="color: rgba(0, 0, 255, 1)">fn</span><span style="color: rgba(0, 0, 0, 1)">(i32) </span><span style="color: rgba(0, 0, 0, 1)">-&gt;</span><span style="color: rgba(0, 0, 0, 1)"> i32, arg: i32) </span><span style="color: rgba(0, 0, 0, 1)">-&gt;</span><span style="color: rgba(0, 0, 0, 1)"> i32 </span></div>
<div><span style="color: rgba(0, 0, 0, 1)">这种定义方式其实某种程度上更加符合我的思维方式。</span></div>
<div>&nbsp;</div>
<div><span style="color: rgba(0, 0, 0, 1)">在java中,我们不能那么定义,最多只能定义一个函数接口为参数,例如:</span></div>
<div>&nbsp;</div>
<div><span style="color: rgba(0, 0, 0, 1)"><code>void Fight(Tool tool,Target tar)</code><br></span></div>
</div>
<p>它看起来不如rust来的那么明显。rust的定义能够让我立刻意识到这是函数指针参数(意味着这是一段代码)。</p>
<p>当然,只要愿意,应该也可以把函数指针以不那么明显的方式定义,后文有例子.</p>
<h1>二、示例</h1>
<pre class="highlighter-prismjs prismjs-lines-highlighted language-rust" tabindex="0"><code>fn add_one(x: i32) -&gt; i32 {
    x + 1
}
fn multiply_10(x: i32) -&gt; i32 {
    x * 10
}

typeFnPointer = fn(i32,i32) -&gt; String;

fnlink(f: FnPointer, x:i32,y:i32) {
   let dst= f(x,y);
   println!("{}",dst);
}

/**
* 第一个参数是函数指针,fn指针实现了FnOnce,FnMut,Fn特质,所以可以接受匿名函数作为参数。
*/
fn do_twice(f: fn(i32) -&gt; i32, arg: i32) -&gt; i32 {
    f(arg) + f(arg)
}


fn 书本例子() {
    let list_of_numbers = vec!;
    //利用rustc,象我这样的傻瓜也能写rust!
    let list_of_strings: Vec&lt;String&gt; =
      list_of_numbers.iter().map(|i| "0".to_owned()+&amp;i.to_string()).collect();
    println!("{:?}", list_of_strings);
    let list_of_strings2: Vec&lt;String&gt; =
      list_of_numbers.iter().map(ToString::to_string).collect();
    println!("{:?}", list_of_strings2);
}


fn main() {
    //封装再封装,就可以解决一些复杂问题,至于是否高效是另外一回事了.
    let funcs=;
    let numbers = ;
    for i in 0..numbers.len() {
      let answer1 = do_twice(funcs, numbers);
      let answer2=do_twice(funcs, numbers);
      println!("({}+1)*2= {},({}*10)*2={}",numbers,answer1,numbers,answer2);   
    }

    // do_twice同时也能接受匿名函数,rust的解释:fn指针实现了FnOnce,FnMut,Fn特质,所以可以接受匿名函数作为参数。
   
    println!("\n匿名函数也能被接受作为参数的演示....");
    let answer3 = do_twice(|x| x * 2+99, numbers);
    println!("{}经过处理后等于{}",numbers,answer3);

    // 书本例子,让我们感兴趣是类似java等语言的流式操作
    书本例子();

    //类型别名
    let link_to = |x:i32,y:i32| x.to_string()+&amp;y.to_string();
    link(link_to, 123,899);
}</code></pre>
<p>&nbsp;</p>
<p>上例基本模仿书本,主要演示两个问题:</p>
<p>1.函数指针如何定义</p>
<p>2.使用函数指针作为参数的函数,也能接收匿名函数为入参</p>
<p>另外顺便演示了如何让函数指针看起来不那么明显(效果上类似java等一些语言),以下代码用于达成这个目的:</p>
<pre class="highlighter-prismjs prismjs-lines-highlighted language-rust" tabindex="0"><code>typeFnPointer = fn(i32,i32) -&gt; String;
fnlink(f: FnPointer, x:i32,y:i32) {
   let dst= f(x,y);
   println!("{}",dst);
}</code></pre>
<p>&nbsp;</p>
<p>如果函数指针比较复杂,也许用上别名也不错。</p>
<p>输出结果:</p>
<p><img src="https://img2024.cnblogs.com/blog/1177268/202504/1177268-20250410173759969-448960698.png"></p>
<h1>三、函数指针的替代方案</h1>
<p>在函数/方法中除了可以通过指定函数指针的方式来传递函数,其实还有几种方式:</p>
<p>1.利用特质绑定+通用类型</p>
<p>2.还是特质绑定+通用类型,只不过是另外一种书写形式</p>
<p>3.使用特质对象+动态分发dyn</p>
<p>以下就是例子:</p>
<pre class="highlighter-prismjs prismjs-lines-highlighted language-rust" tabindex="0"><code>/**
* 函数指针的替代者1-- 利用特质绑定
*/
fn batman&lt;T: FnOnce(i32,i32)-&gt;String&gt;(f:T) {
    let result=f(1094,101);
    println!("{}",result);
}
/**
* 函数指针的替代者2 -- 特质绑定的简化写法,rust 1.13引入的trait bound syntax
*/
fn eggman(f:impl FnOnce(i32,i32)-&gt;String) {
    let result=f(1094,103);
    println!("{}",result);
}
/**
* 函数指针的替代者3 -- 使用特质对象
*/
fn pigman(f:&amp;dyn Fn(i32,i32)-&gt;String) {
    let result=f(2094,103);
    println!("{}",result);
}

fn main(){
    batman(|x:i32,y:i32| x.to_string()+&amp;y.to_string()+" 黑蝙蝠侠 🦇");
    eggman(|x:i32,y:i32| x.to_string()+&amp;y.to_string()+" 蛋蛋大侠 🥚🥚");
    pigman(&amp;|x:i32,y:i32| x.to_string()+&amp;y.to_string()+" 猪猪侠士 🐷");   
}</code></pre>
<p>&nbsp;</p>
<p>输出结果:</p>
<p><img src="https://img2024.cnblogs.com/blog/1177268/202504/1177268-20250410185459160-1030846689.png"></p>
<p>这里例子中,比较特殊的就是pigman的定义:</p>
<div style="color: rgba(101, 123, 131, 1); background-color: rgba(253, 246, 227, 1); font-family: Consolas, &quot;Courier New&quot;, monospace; font-weight: normal; font-size: 14px; line-height: 19px; white-space: pre">
<div><span style="color: rgba(133, 153, 0, 1)">fn</span><span style="color: rgba(101, 123, 131, 1)"> </span><span style="color: rgba(38, 139, 210, 1)">pigman</span><span style="color: rgba(101, 123, 131, 1)">(</span><span style="color: rgba(38, 139, 210, 1)">f</span><span style="color: rgba(133, 153, 0, 1)">:&amp;</span><span style="color: rgba(133, 153, 0, 1)">dyn</span><span style="color: rgba(101, 123, 131, 1)"> </span><span style="color: rgba(38, 139, 210, 1)">Fn</span><span style="color: rgba(101, 123, 131, 1)">(</span><span style="color: rgba(203, 75, 22, 1)">i32</span><span style="color: rgba(101, 123, 131, 1)">,</span><span style="color: rgba(203, 75, 22, 1)">i32</span><span style="color: rgba(101, 123, 131, 1)">)</span><span style="color: rgba(133, 153, 0, 1)">-&gt;</span><span style="color: rgba(203, 75, 22, 1)">String</span><span style="color: rgba(101, 123, 131, 1)">) </span></div>
</div>
<p>这里的定义比较特殊,用上了&amp;dyn,为什么要这样了?</p>
<ul>
<li>因为特质对象必须使用动态分发,这是rust规定的</li>
<li>要使用&amp;,是因为特质对象大小不确定,rustc不知道大小,所以只能加一个引用,以便构成一个指针(编译器可以知道大小)</li>
</ul>
<p>最后,pigman用了Fn,而不是FnOnce,这是因为FnOnce要求移出,要求移出则必须实现Copy。为了不Copy,所以改为Fn。</p>
<p>记住,rust中函数/方法本身也是一种类型。</p>
<h1>四、小结</h1>
<p>1.函数指针fn的存在某种程度上方便了程序设计-这让程序设计更加灵活</p>
<p>2.函数指针实现了FnOnce,FnMut,Fn,按照rust的说法就是可以定义一个匿名函数作为函数指针传递给函数/方法</p>
<p>3.函数指针的存在,使得编写代码某种程度上方便了部分程序员</p>
<p>4.事实上,就是没有函数指针,其实也有其它方式能过实现类似的效果</p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:正在战斗中,转载请注明原文链接:https://www.cnblogs.com/lzfhope/p/18819057</p><br><br>
来源:https://www.cnblogs.com/lzfhope/p/18819057
頁: [1]
查看完整版本: rust学习二十.13、RUST的函数指针fn和匿名函数