Rust中函数的定义位置是没有限制的,不必先声明再定义。 本章原文
fn main ( ) {
fizzbuzz_to ( 100 ) ;
}
fn is_divisible_by ( lhs: u32 , rhs: u32 ) -> bool {
if rhs == 0 {
return false ;
}
lhs % rhs == 0
}
fn fizzbuzz ( n: u32 ) -> ( ) {
if is_divisible_by ( n, 15 ) {
println! ( "fizzbuzz" ) ;
} else if is_divisible_by ( n, 3 ) {
println! ( "fizz" ) ;
} else if is_divisible_by ( n, 5 ) {
println! ( "buzz" ) ;
} else {
println! ( "{}" , n) ;
}
}
fn fizzbuzz_to ( n: u32 ) {
for n in 1 ..= n {
fizzbuzz ( n) ;
}
}
方法
方法在结构体中常常使用,使用impl
进行定义,同时又由关键字self
进行访问。 有两种方法的定义方法,一种是静态方法,一种是实例方法,调用方式,静态方法使用"::
“调用,实例方法使用”.
"调用 实例方法的三种调用方式:
&self self &mut self 调用之后仍然可用 调用之后如果释放了,不可用 调用之后可以改变对像的值
struct Point {
x: f64 ,
y: f64 ,
}
impl Point {
fn origin ( ) -> Point {
Point { x: 0.0 , y: 0.0 }
}
fn new ( x: f64 , y: f64 ) -> Point {
Point { x: x, y: y }
}
}
struct Rectangle {
p1: Point ,
p2: Point ,
}
impl Rectangle {
fn area ( & self ) -> f64 {
let Point { x: x1, y: y1 } = self . p1;
let Point { x: x2, y: y2 } = self . p2;
( ( x1 - x2) * ( y1 - y2) ) . abs ( )
}
fn perimeter ( & self ) -> f64 {
let Point { x: x1, y: y1 } = self . p1;
let Point { x: x2, y: y2 } = self . p2;
2.0 * ( ( x1 - x2) . abs ( ) + ( y1 - y2) . abs ( ) )
}
fn translate ( & mut self , x: f64 , y: f64 ) {
self . p1. x += x;
self . p2. x += x;
self . p1. y += y;
self . p2. y += y;
}
}
struct Pair ( Box < i32 > , Box < i32 > ) ;
impl Pair {
fn destroy ( self ) {
let Pair ( first, second) = self ;
println! ( "Destroying Pair({}, {})" , first, second) ;
}
}
fn main ( ) {
let rectangle = Rectangle {
p1: Point :: origin ( ) ,
p2: Point :: new ( 3.0 , 4.0 ) ,
} ;
println! ( "Rectangle perimeter: {}" , rectangle. perimeter ( ) ) ;
println! ( "Rectangle area: {}" , rectangle. area ( ) ) ;
let mut square = Rectangle {
p1: Point :: origin ( ) ,
p2: Point :: new ( 1.0 , 1.0 ) ,
} ;
square. translate ( 1.0 , 1.0 ) ;
let pair = Pair ( Box :: new ( 1 ) , Box :: new ( 2 ) ) ;
pair. destroy ( ) ;
}
闭包
闭包产生的类型就叫做闭包类型,和引用类型不同。 闭包常常在临时使用时使用。 建议使用闭包的时候尽量标注类型。 闭包和函数使用方法差不多,但闭包能干的事情不如函数多。
fn main ( ) {
fn function ( i: i32 ) -> i32 { i + 1 }
let closure_annotated = | i: i32 | -> i32 { i + 1 } ;
let closure_inferred = | i | i + 1 ;
let i = 1 ;
println! ( "function: {}" , function ( i) ) ;
println! ( "closure_annotated: {}" , closure_annotated ( i) ) ;
println! ( "closure_inferred: {}" , closure_inferred ( i) ) ;
let one = | | 1 ;
println! ( "closure returning one: {}" , one ( ) ) ;
}
闭包的捕获
闭包的捕获可以通过以下方式捕获变量,或者使用move(移动)变量。 默认状态下是使用借用调用,也可以添加关键字mut或者move直接获取。
fn main ( ) {
use std:: mem;
let color = String :: from ( "green" ) ;
let print = | | println! ( "`color`: {}" , color) ;
print ( ) ;
let _reborrow = & color;
print ( ) ;
let _color_moved = color;
let mut count = 0 ;
let mut inc = | | {
count += 1 ;
println! ( "`count`: {}" , count) ;
} ;
inc ( ) ;
inc ( ) ;
let _count_reborrowed = & mut count;
let movable = Box :: new ( 3 ) ;
let consume = | | {
println! ( "`movable`: {:?}" , movable) ;
mem:: drop ( movable) ;
} ;
consume ( ) ;
}
在闭包没有调用完成的过程中不能使用变量转移引用。 使用move会强制获取所有权,原来的闭包将不能使用。
fn main ( ) {
let haystack = vec! [ 1 , 2 , 3 ] ;
let contains = move | needle| haystack. contains ( needle) ;
println! ( "{}" , contains ( & 1 ) ) ;
println! ( "{}" , contains ( & 4 ) ) ;
}
闭包作为输入参数
闭包在作为输入参数的时候需要指出完整的闭包类型,通过使用下面的trait
来指定:
Fn FnMut FnOnce 表示捕获方式为通过引用(&T)的闭包 表示捕获方式为通过可变引用(&mut T)的闭包 表示捕获方式通过值(T)的闭包
闭包在使用的过程中,编译器使用以限制最多的方式捕获 例如用一个类型说明为 FnOnce
的闭包作为参数。这说明闭包可能采取 &T
,&mut T
或 T
中的一种捕获方式,但编译器最终是根据所捕获变量在闭包里的使用情况决定捕获方式。 这是因为如果能以移动的方式捕获变量,则闭包也有能力使用其他方式借用变量。注意反过来就不再成立:如果参数的类型说明是 Fn
,那么不允许该闭包通过 &mut T
或 T
捕获变量。
fn apply < F > ( f: F ) where
F : FnOnce ( ) {
f ( ) ;
}
fn apply_to_3 < F > ( f: F ) -> i32 where
F : Fn ( i32 ) -> i32 {
f ( 3 )
}
fn main ( ) {
use std:: mem;
let greeting = "hello" ;
let mut farewell = "goodbye" . to_owned ( ) ;
let diary = | | {
println! ( "I said {}." , greeting) ;
farewell. push_str ( "!!!" ) ;
println! ( "Then I screamed {}." , farewell) ;
println! ( "Now I can sleep. zzzzz" ) ;
mem:: drop ( farewell) ;
} ;
apply ( diary) ;
let double = | x| 2 * x;
println! ( "3 doubled: {}" , apply_to_3 ( double) ) ;
}
类型匿名
fn apply < F > ( f: F ) where
F : FnOnce ( ) {
f ( ) ;
}
闭包定义时,编译器会隐式创建一个匿名类型的结构体,来储存闭包捕获的变量,同时为这个未知类型的结构体实现函数功能,通过Fn
、FnMut
、FnOnce
中的三种trait
中的一种。 但是使用闭包作为参数是必须指定trait类型是三种中的哪一个,来进行限制。
fn apply < F > ( f: F ) where
F : Fn ( ) {
f ( ) ;
}
fn main ( ) {
let x = 7 ;
let print = | | println! ( "{}" , x) ;
apply ( print) ;
}
输入参数
某个闭包作为参数,则这个函数也可以使用满足闭包约束(Fn、FnOnce、FnMut)的函数作为参数。
fn call_me < F : Fn ( ) > ( f: F ) {
f ( ) ;
}
fn function ( ) {
println! ( "I'm a function!" ) ;
}
fn main ( ) {
let closure = | | println! ( "I'm a closure!" ) ;
call_me ( closure) ;
call_me ( function) ;
}
作为输出参数
闭包可以作为输入参数,但是作为输出参数只能使用impl trait
,因为输出参数只能返回具体值(非泛型)。 这三个trait便是Fn、FnOnce、FnMut。 同时必须使用move关键字,(因为这表明这些闭包是使用具体值进行,而函数在退出时也需要清空所有的引用,如果不使用会造成无效引用)
fn create_fn ( ) -> impl Fn ( ) {
let text = "Fn" . to_owned ( ) ;
move | | println! ( "This is a: {}" , text)
}
fn create_fnmut ( ) -> impl FnMut ( ) {
let text = "FnMut" . to_owned ( ) ;
move | | println! ( "This is a: {}" , text)
}
fn create_fnonce ( ) -> impl FnOnce ( ) {
let text = "FnOnce" . to_owned ( ) ;
move | | println! ( "This is a: {}" , text)
}
fn main ( ) {
let fn_plain = create_fn ( ) ;
let mut fn_mut = create_fnmut ( ) ;
let fn_once = create_fnonce ( ) ;
fn_plain ( ) ;
fn_mut ( ) ;
fn_once ( ) ;
}
Iterator::any
这里要注意的是any函数的签名,返回值是bool,但是在函数内部,变量是通过值传递给闭包,这个是迭代器对应元素的类型。输入值最多只能被修改不能被消耗。(这也与之前编译器以限制最多的方法进行设限相对应)
Iterator::find
注意find函数会将迭代器的引用传递给闭包。所以会出现解构((&&x) x ==2)
之类的。
fn main ( ) {
let vec1 = vec! [ 1 , 2 , 3 ] ;
let vec2 = vec! [ 4 , 5 , 6 ] ;
let mut iter = vec1. iter ( ) ;
let mut into_iter = vec2. into_iter ( ) ;
println! ( "Find 2 in vec1: {:?}" , iter . find ( | && x| x == 2 ) ) ;
println! ( "Find 2 in vec2: {:?}" , into_iter. find ( | & x| x == 2 ) ) ;
let array1 = [ 1 , 2 , 3 ] ;
let array2 = [ 4 , 5 , 6 ] ;
println! ( "Find 2 in array1: {:?}" , array1. iter ( ) . find ( | && x| x == 2 ) ) ;
println! ( "Find 2 in array2: {:?}" , array2. into_iter ( ) . find ( | & x| x == 2 ) ) ;
}
高阶函数
这种高阶函数是函数中输入一个或者多个函数,然后产生一个更厉害的函数。这是函数式风格编程的特点。
fn is_odd ( n: u32 ) -> bool {
n % 2 == 1
}
fn main ( ) {
println! ( "Find the sum of all the squared odd numbers under 1000" ) ;
let upper = 1000 ;
let mut acc = 0 ;
for n in 0 .. {
let n_squared = n * n;
if n_squared >= upper {
break ;
} else if is_odd ( n_squared) {
acc += n_squared;
}
}
println! ( "imperative style: {}" , acc) ;
let sum_of_squared_odd_numbers: u32 =
( 0 .. ) . map ( | n| n * n)
. take_while ( | & n| n < upper)
. filter ( | & n| is_odd ( n) )
. fold ( 0 , | sum, i| sum + i) ;
println! ( "functional style: {}" , sum_of_squared_odd_numbers) ;
}
发散函数
发散函数没有返回值,使用!
标记,这是空类型 这种类型无法实例化,因为可能具有的所有可能值集合为空。和()类型不同,后者只有一个可能的值。
fn foo ( ) -> ! {
panic! ( "This call never returns." ) ;
}
下面的例子可以正常返回,说明了空类型和()类型的不同。
fn some_fn ( ) {
( )
}
fn main ( ) {
let a: ( ) = some_fn ( ) ;
println! ( "This function returns and you can see this line." )
}
#![feature(never_type)]
fn main ( ) {
let x: ! = panic! ( "This call never returns." ) ;
println! ( "You will never see this line!" ) ;
}
match分支中使用发散函数,因为continue不会返回任何值。
fn main ( ) {
fn sum_odd_numbers ( up_to: u32 ) -> u32 {
let mut acc = 0 ;
for i in 0 .. up_to {
let addition: u32 = match i% 2 == 1 {
true => i,
false => continue ,
} ;
acc += addition;
}
acc
}
println! ( "Sum of odd numbers up to 9 (excluding): {}" , sum_odd_numbers ( 9 ) ) ;
}
这也是永远循环(如 loop {}
)的函数(如网络服务器)或终止进程的函数(如 exit()
)的返回类型。